import datetime

import pytest

from undertime import (
    OffsetZone,
    UndertimeArgumentParser,
    compute_table,
    guess_zone,
    main,
    parse_date,
)

BASE_ARGV = ["--config", "/dev/null", "--no-default"]


def test_main_two_corners(capsys):
    """This tests two corner cases, still to be documented."""
    argv = BASE_ARGV + ["--no-unique"]
    date = ["--date", "2020-03-08 12:00:00-04:00"]
    zones = ["--timezone", "EST", "America/New_York", "UTC"]
    args = UndertimeArgumentParser().parse_args(argv + date + zones)
    main(args)
    captured = capsys.readouterr()
    assert captured.out.split("\n")[:13] == [
        "╔════════╤════════════════════╤════════╤═══════════╗",
        "║  EST   │  America/New_York  │  UTC   │   overlap ║",
        "╠════════╪════════════════════╪════════╪═══════════╣",
        "║ 19:00  │       19:00        │ 00:00  │         0 ║",
        "║ 20:00  │       20:00        │ 01:00  │         0 ║",
        "║ 21:00  │       21:00        │ 02:00  │         0 ║",
        "║ 22:00  │       22:00        │ 03:00  │         0 ║",
        "║ 23:00  │       23:00        │ 04:00  │         0 ║",
        "║ 00:00  │       00:00        │ 05:00  │         0 ║",
        "║ 01:00  │       01:00        │ 06:00  │         0 ║",
        "║ 02:00  │       03:00        │ 07:00  │         0 ║",
        "║ 03:00  │       04:00        │ 08:00  │         0 ║",
        "║ 04:00  │       05:00        │ 09:00_ │         1 ║",
    ]
    assert captured.out.split("\n")[-6:-1] == [
        "local time: 2020-03-08 16:00:00+00:00",
        "equivalent to:",
        "- 11:00 EST",
        "- 12:00 America/New_York",
        "- 16:00 UTC",
    ]


def test_main_overlap_truncate(capsys):
    date = ["--date", "2020-11-01 12:00:00-04:00"]
    argv = BASE_ARGV + ["--no-unique"]
    zones = ["--timezone", "EST", "America/New_York", "UTC"]
    args = UndertimeArgumentParser().parse_args(argv + date + zones)
    main(args)
    captured = capsys.readouterr()
    assert captured.out.split("\n")[:13] == [
        "╔════════╤════════════════════╤════════╤═══════════╗",
        "║  EST   │  America/New_York  │  UTC   │   overlap ║",
        "╠════════╪════════════════════╪════════╪═══════════╣",
        "║ 19:00  │       20:00        │ 00:00  │         0 ║",
        "║ 20:00  │       21:00        │ 01:00  │         0 ║",
        "║ 21:00  │       22:00        │ 02:00  │         0 ║",
        "║ 22:00  │       23:00        │ 03:00  │         0 ║",
        "║ 23:00  │       00:00        │ 04:00  │         0 ║",
        "║ 00:00  │       01:00        │ 05:00  │         0 ║",
        "║ 01:00  │       01:00        │ 06:00  │         0 ║",
        "║ 02:00  │       02:00        │ 07:00  │         0 ║",
        "║ 03:00  │       03:00        │ 08:00  │         0 ║",
        "║ 04:00  │       04:00        │ 09:00_ │         1 ║",
    ]
    assert captured.out.split("\n")[-6:-1] == [
        "local time: 2020-11-01 16:00:00+00:00",
        "equivalent to:",
        "- 11:00 EST",
        "- 11:00 America/New_York",
        "- 16:00 UTC",
    ]
    extra = ["--no-overlap", "--truncate"]
    args = UndertimeArgumentParser().parse_args(argv + date + zones + extra)
    main(args)
    captured = capsys.readouterr()
    assert captured.out.startswith(
        """╔════════╤════════════╤════════╗
║  EST   │  New_York  │  UTC   ║
╠════════╪════════════╪════════╣
║ 19:00  │   20:00    │ 00:00  ║"""
    )
    extra = ["--overlap", "--truncate"]
    args = UndertimeArgumentParser().parse_args(argv + date + zones + extra)
    main(args)
    captured = capsys.readouterr()
    assert captured.out.startswith(
        """╔════════╤════════════╤════════╤═════╗
║  EST   │  New_York  │  UTC   │   n ║
╠════════╪════════════╪════════╪═════╣
║ 19:00  │   20:00    │ 00:00  │   0 ║"""
    )


def test_main_no_table(capsys):
    date = ["--date", "2020-11-01 12:00:00-04:00"]
    argv = BASE_ARGV + ["--no-unique"]
    zones = ["--timezone", "EST", "America/New_York", "UTC"]
    extra = ["--no-table"]
    args = UndertimeArgumentParser().parse_args(argv + date + zones + extra)
    main(args)
    captured = capsys.readouterr()
    assert (
        captured.out
        == """UTC time: 2020-11-01 16:00:00+00:00
local time: 2020-11-01 16:00:00+00:00
equivalent to:
- 11:00 EST
- 11:00 America/New_York
- 16:00 UTC
"""
    )


def test_main_sorted_equivalent(capsys):
    """test the timezone sort functionality"""
    date = ["--date", "2020-11-01 12:00:00-04:00"]
    argv = BASE_ARGV + ["--no-unique"]
    zones = ["--timezone", "US/Pacific", "UTC", "US/Eastern"]
    extra = ["--no-table"]
    args = UndertimeArgumentParser().parse_args(argv + date + zones + extra)
    main(args)
    captured = capsys.readouterr()
    assert (
        captured.out
        == """UTC time: 2020-11-01 16:00:00+00:00
local time: 2020-11-01 16:00:00+00:00
equivalent to:
- 08:00 US/Pacific
- 11:00 US/Eastern
- 16:00 UTC
"""
    )


def test_main_next_day(capsys):
    date = ["--date", "2020-11-01 12:00:00-04:00"]
    argv = BASE_ARGV + ["--no-unique"]
    zones = ["--timezone", "EST", "America/New_York", "UTC", "Pacific/Auckland"]
    extra = ["--no-table"]
    args = UndertimeArgumentParser().parse_args(argv + date + zones + extra)
    main(args)
    captured = capsys.readouterr()
    assert (
        captured.out
        == """UTC time: 2020-11-01 16:00:00+00:00
local time: 2020-11-01 16:00:00+00:00
equivalent to:
- 11:00 EST
- 11:00 America/New_York
- 16:00 UTC
- 2020-11-02 05:00 Pacific/Auckland
"""
    )


def test_main_invalids(capsys):
    date = ["--date", "2020-11-01 12:00:00-04:00"]
    args = UndertimeArgumentParser().parse_args(
        BASE_ARGV + date + ["--timezones", "CEST"]
    )
    with pytest.raises(SystemExit) as excinfo:
        main(args)
        assert excinfo.value.code == 1
        captured = capsys.readouterr()
        assert "unknown zone, skipping: CEST" in captured.err
    args = UndertimeArgumentParser().parse_args(BASE_ARGV)
    with pytest.raises(SystemExit) as excinfo:
        main(args)
        assert excinfo.value.code == 1
    args = UndertimeArgumentParser().parse_args(BASE_ARGV + ["NOTADATE"])
    with pytest.raises(SystemExit) as excinfo:
        main(args)
        captured = capsys.readouterr()
        assert excinfo.value.code == 1
        assert "No valid time zone found." in captured.err


def test_list_zones(capsys):
    args = UndertimeArgumentParser().parse_args(BASE_ARGV + ["--list-zones"])
    main(args)
    captured = capsys.readouterr()
    assert "Africa/Abidjan" in captured.out
    assert "Zulu" in captured.out


def test_version(capsys):
    with pytest.raises(SystemExit) as exc:
        UndertimeArgumentParser().parse_args(BASE_ARGV + ["--version"])
        assert exc.value.code == 0


def test_offset_sonze():
    assert OffsetZone("UTC+2")._minutes // 60 == 2
    assert OffsetZone("GMT-4")._minutes // 60 == -4
    assert OffsetZone("UTC+3:30")._minutes == 210
    assert OffsetZone("UTC+00:30")._minutes == 30
    assert OffsetZone("UTC-00:30")._minutes == -30
    assert OffsetZone("UTC-01:30")._minutes == -90
    with pytest.raises(ValueError) as excinfo:
        OffsetZone("UTC-13")
        assert "Hours cannot be bigger than 12: 13" in str(excinfo.value)
    with pytest.raises(ValueError) as excinfo:
        OffsetZone("GMT+20")
        assert "Hours cannot be bigger than 12: 20" == excinfo.value
    with pytest.raises(AssertionError):
        OffsetZone("America/Eastern")
        # AssertionError
    with pytest.raises(ValueError) as excinfo:
        OffsetZone("UTC+3:70")._minutes // 60
        assert "Minute cannot be bigger than 59: 70" == excinfo.value
    with pytest.raises(AssertionError) as excinfo:
        OffsetZone("UTC+3,30")
        # AssertionError
    with pytest.raises(AssertionError) as excinfo:
        OffsetZone("GMT-3.14159")


def test_compute_table():
    local_zone = datetime.timezone(datetime.timedelta(days=-1, seconds=72000), "EDT")
    now = parse_date("2020-03-08 22:30", local_zone=local_zone)
    nearest_hour = now.replace(minute=0, second=0, microsecond=0)
    assert nearest_hour == datetime.datetime(2020, 3, 8, 22, 0, tzinfo=local_zone)
    assert nearest_hour.replace(hour=0) == datetime.datetime(
        2020, 3, 8, 0, 0, tzinfo=local_zone
    )
    timezones = []
    timezones.append(local_zone)
    timezones += [guess_zone(z) for z in ("America/New_York", "UTC")]
    assert [str(t) for t in timezones] == ["EDT", "America/New_York", "UTC"]

    subset = [
        # fmt: off
        cell[:5]
        for row in compute_table(now, timezones, 9, 17)[1:5]
        for cell in row
        # fmt: on
    ]
    assert subset == [
        # fmt: off
        "00:00", "23:00", "04:00", "0",
        "01:00", "00:00", "05:00", "0",
        "02:00", "01:00", "06:00", "0",
        "03:00", "03:00", "07:00", "0",
        # fmt: on
    ]
    subset = [
        cell[:5]
        for row in compute_table(now, timezones, 9, 17, 0, False)[1:5]
        for cell in row
    ]
    assert subset == [
        # fmt: off
        "00:00", "23:00", "04:00",
        "01:00", "00:00", "05:00",
        "02:00", "01:00", "06:00",
        "03:00", "03:00", "07:00",
        # fmt: on
    ]
