import csv
import json
from pathlib import Path
import tempfile
import unittest
from unittest.mock import Mock

from inventory import collect_live, main, rows_from_pages, write_report


class InventoryTests(unittest.TestCase):
    def setUp(self):
        self.pages = json.loads(Path(__file__).with_name("sample-pages.json").read_text())

    def test_all_pages_and_missing_tags(self):
        client = Mock()
        client.get_paginator.return_value.paginate.return_value = iter(self.pages)
        rows = collect_live(client, "us-east-1")
        self.assertEqual([row["id"] for row in rows], ["i-example1", "i-example2"])
        self.assertEqual(rows[1]["name"], "unnamed")
        client.get_paginator.assert_called_once_with("describe_instances")

    def test_order_independent_of_page_order(self):
        self.assertEqual(rows_from_pages(self.pages, "us-east-1"),
                         rows_from_pages(reversed(self.pages), "us-east-1"))

    def test_empty_inventory_writes_header(self):
        with tempfile.TemporaryDirectory() as folder:
            output = Path(folder) / "report.csv"
            write_report(rows_from_pages([{"Reservations": []}], "us-east-1"), output)
            with output.open(newline="") as stream:
                self.assertEqual(list(csv.reader(stream)),
                                 [["region", "id", "state", "type", "name"]])

    def test_cli_fixture_and_existing_output(self):
        with tempfile.TemporaryDirectory() as folder:
            fixture = Path(folder) / "pages.json"
            fixture.write_text(json.dumps(self.pages))
            output = Path(folder) / "report.csv"
            argv = ["--fixture", str(fixture), "--region", "us-east-1", "--output", str(output)]
            self.assertEqual(main(argv), 0)
            original = output.read_bytes()
            self.assertEqual(main(argv), 1)
            self.assertEqual(output.read_bytes(), original)

    def test_malformed_or_missing_fixture_creates_no_report(self):
        with tempfile.TemporaryDirectory() as folder:
            fixture = Path(folder) / "bad.json"
            output = Path(folder) / "report.csv"
            argv = ["--fixture", str(fixture), "--region", "us-east-1", "--output", str(output)]
            self.assertEqual(main(argv), 1)
            for data in ["not JSON", "{}", '[{"Reservations": null}]']:
                fixture.write_text(data)
                self.assertEqual(main(argv), 1)
                self.assertFalse(output.exists())

    def test_partial_report_removed_on_invalid_row(self):
        with tempfile.TemporaryDirectory() as folder:
            output = Path(folder) / "report.csv"
            with self.assertRaises(ValueError):
                write_report([{"unexpected": "field"}], output)
            self.assertFalse(output.exists())

    def test_pagination_failure_is_not_empty_success(self):
        client = Mock()
        client.get_paginator.return_value.paginate.side_effect = RuntimeError("API failed")
        with self.assertRaisesRegex(RuntimeError, "API failed"):
            collect_live(client, "us-east-1")


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