"""EC2 inventory exercise: offline fixtures by default, explicit live mode."""
import argparse
import csv
import json
import logging
from pathlib import Path

FIELDS = ["region", "id", "state", "type", "name"]


def rows_from_pages(pages, region):
    rows = []
    for page in pages:
        for reservation in page.get("Reservations", []):
            for instance in reservation.get("Instances", []):
                tags = {tag["Key"]: tag["Value"]
                        for tag in instance.get("Tags", [])}
                rows.append({"region": region,
                             "id": instance["InstanceId"],
                             "state": instance["State"]["Name"],
                             "type": instance["InstanceType"],
                             "name": tags.get("Name", "unnamed")})
    return sorted(rows, key=lambda row: (row["region"], row["id"]))


def collect_live(client, region):
    pages = client.get_paginator("describe_instances").paginate()
    return rows_from_pages(pages, region)


def write_report(rows, output):
    # Exclusive creation prevents accidental replacement of a previous report.
    created = False
    try:
        with output.open("x", encoding="utf-8", newline="") as stream:
            created = True
            writer = csv.DictWriter(stream, fieldnames=FIELDS)
            writer.writeheader()
            writer.writerows(rows)
    except (OSError, ValueError, csv.Error):
        if created:
            output.unlink(missing_ok=True)
        raise


def main(argv=None):
    parser = argparse.ArgumentParser(description=__doc__)
    source = parser.add_mutually_exclusive_group(required=True)
    source.add_argument("--fixture", type=Path, help="JSON array of API pages")
    source.add_argument("--live", action="store_true", help="Read AWS EC2 inventory")
    parser.add_argument("--region", required=True)
    parser.add_argument("--profile", help="Optional AWS profile for live mode")
    parser.add_argument("--output", type=Path, default=Path("inventory.csv"))
    args = parser.parse_args(argv)
    if args.profile and not args.live:
        parser.error("--profile requires --live")
    logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")

    try:
        if args.fixture:
            with args.fixture.open(encoding="utf-8") as stream:
                pages = json.load(stream)
            if not isinstance(pages, list):
                raise ValueError("Fixture must be a JSON array of API pages")
            rows = rows_from_pages(pages, args.region)
        else:
            try:
                import boto3
                from botocore.config import Config
                from botocore.exceptions import BotoCoreError, ClientError
            except ImportError:
                logging.error("Live mode requires boto3: python -m pip install boto3")
                return 1
            try:
                session = boto3.Session(profile_name=args.profile)
                config = Config(connect_timeout=5, read_timeout=20,
                                retries={"mode": "standard", "total_max_attempts": 4})
                client = session.client("ec2", region_name=args.region, config=config)
                rows = collect_live(client, args.region)
            except (BotoCoreError, ClientError) as error:
                logging.error("AWS inventory failed: %s", error)
                return 1
        write_report(rows, args.output)
    except (OSError, ValueError, KeyError, TypeError, AttributeError, csv.Error) as error:
        logging.error("Report failed: %s", error)
        return 1
    logging.info("region=%s instances=%d output=%s", args.region, len(rows), args.output)
    print(f"Wrote {len(rows)} instances to {args.output}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
