import io
import json
import subprocess
import sys
import unittest
from datetime import datetime, timezone
from pathlib import Path

from verify_dates import BUSINESS_TZ, belongs_to_day, day_bounds, parse_timestamp, select_rows


ROOT = Path(__file__).resolve().parent
EVENTS = (ROOT / "events.csv").read_text()


class DateTests(unittest.TestCase):
    def test_known_fixture(self):
        result = select_rows(io.StringIO(EVENTS), "2026-10-12")
        self.assertEqual(result["included_ids"], ["E02", "E03", "E05", "E06"])
        self.assertEqual(result["excluded_ids"], ["E01", "E04"])
        self.assertEqual(result["input_rows"], 6)

    def test_start_included(self):
        self.assertTrue(belongs_to_day("2026-10-11T16:00:00Z", "2026-10-12"))

    def test_before_start_excluded(self):
        self.assertFalse(belongs_to_day("2026-10-11T15:59:59.999999Z", "2026-10-12"))

    def test_last_microsecond_included(self):
        self.assertTrue(belongs_to_day("2026-10-12T15:59:59.999999Z", "2026-10-12"))

    def test_next_day_start_excluded(self):
        self.assertFalse(belongs_to_day("2026-10-12T16:00:00Z", "2026-10-12"))

    def test_same_instant_different_offsets(self):
        self.assertEqual(parse_timestamp("2026-10-11T16:00:00Z"),
                         parse_timestamp("2026-10-12T00:00:00+08:00"))

    def test_negative_offset(self):
        self.assertTrue(belongs_to_day("2026-10-11T11:00:00-05:00", "2026-10-12"))

    def test_date_prefix_is_not_business_date(self):
        value = "2026-10-11T16:00:00Z"
        self.assertNotEqual(value[:10], "2026-10-12")
        self.assertTrue(belongs_to_day(value, "2026-10-12"))

    def test_timezone_replace_is_not_conversion(self):
        original = parse_timestamp("2026-10-11T16:00:00Z")
        self.assertNotEqual(original.replace(tzinfo=BUSINESS_TZ).timestamp(), original.timestamp())
        self.assertEqual(original.astimezone(BUSINESS_TZ).timestamp(), original.timestamp())

    def test_naive_time_rejected(self):
        with self.assertRaisesRegex(ValueError, "explicit_offset"):
            parse_timestamp("2026-10-12T00:00:00")

    def test_locale_and_noncanonical_forms_rejected(self):
        for value in ["12/10/2026 00:00", "2026-10-12 00:00:00Z", "20261012T000000Z"]:
            with self.subTest(value=value), self.assertRaises(ValueError):
                parse_timestamp(value)

    def test_invalid_date_rejected(self):
        with self.assertRaises(ValueError):
            parse_timestamp("2026-02-30T00:00:00Z")

    def test_invalid_offset_minutes_rejected(self):
        with self.assertRaisesRegex(ValueError, "invalid_utc_offset"):
            parse_timestamp("2026-10-12T00:00:00+08:60")

    def test_unknown_negative_zero_offset_rejected(self):
        with self.assertRaisesRegex(ValueError, "unknown_local_offset"):
            parse_timestamp("2026-10-12T00:00:00-00:00")

    def test_leap_day_and_month_rollover(self):
        start, end = day_bounds("2024-02-29")
        self.assertEqual(start.date().isoformat(), "2024-02-29")
        self.assertEqual(end.date().isoformat(), "2024-03-01")

    def test_year_rollover(self):
        self.assertEqual(day_bounds("2026-12-31")[1].date().isoformat(), "2027-01-01")

    def test_day_format_rejected(self):
        for value in ["20261012", "2026-2-1", "2026-10-12T00:00:00"]:
            with self.subTest(value=value), self.assertRaises(ValueError):
                day_bounds(value)

    def test_duplicate_event_id_rejected(self):
        with self.assertRaisesRegex(ValueError, "duplicate_event_id"):
            select_rows(io.StringIO(EVENTS + "E02,2026-10-12T00:00:00Z\n"), "2026-10-12")

    def test_malformed_table_rejected(self):
        for value in ["event_id,occurred_at\n", EVENTS.replace("E01,2026", "E01,extra,2026"),
                      EVENTS.replace("event_id,occurred_at", "id,timestamp")]:
            with self.subTest(value=value), self.assertRaises(ValueError):
                select_rows(io.StringIO(value), "2026-10-12")

    def test_cli_passes_fixture_and_rejects_bad_day(self):
        for day, code in [("2026-10-12", 0), ("2026-02-30", 2)]:
            run = subprocess.run([sys.executable, str(ROOT / "verify_dates.py"),
                                  str(ROOT / "events.csv"), day], capture_output=True, text=True)
            self.assertEqual(run.returncode, code)
            self.assertEqual(json.loads(run.stdout)["valid"], code == 0)


if __name__ == "__main__":
    unittest.main()
