"""Generate plausible demo tickets so the list and the CSV exports look alive.

    python -m scripts.seed_demo          # add the demo set (idempotent)

**Every generated ticket carries a `demo_seed` audit event** naming it as
fabricated. That is deliberate: this app's entire claim is a permanent,
auditable record, so invented activity must be identifiable from inside the
record itself rather than by a naming convention someone can miss.

Find them later:

    SELECT t.number, t.description
    FROM tickets t JOIN ticket_events e ON e.ticket_id = t.id
    WHERE e.kind = 'demo_seed';

There is no purge command by design - tickets are never deleted (that is the
guarantee under test in gate-p0). To clear a demo database, drop the volume and
re-run migrations. Do not add a delete path just to tidy up demo data.
"""

from __future__ import annotations

import random
from datetime import UTC, datetime, timedelta

from sqlalchemy import select

from app.db import SessionLocal
from app.models.tables import (
    Asset,
    Part,
    PartUsage,
    Technician,
    Ticket,
    TicketEvent,
    TicketPriority,
    TicketStatus,
    UsageDirection,
)
from app.services.tickets import next_ticket_number

# Written from Mike's own vocabulary (his flow chart and the CMMS 101 doc):
# the failure, the symptom, and the words a technician would actually use.
SCENARIOS = [
    ("Outfeed bearing is shrieking, getting hot to touch.", "high"),
    ("Modular belt has 4 broken modules on the return side.", "normal"),
    ("Drive motor running at about 80%, high pitched whine for a few days.", "high"),
    ("Right hand wear strip is chewed up and needs replacing.", "normal"),
    ("Gearbox weeping oil onto the floor under the drive end.", "high"),
    ("Chain is slapping, sprocket teeth look hooked.", "normal"),
    ("Guard interlock intermittently faults and stops the line.", "critical"),
    ("Air leak at the cylinder, hissing constantly.", "low"),
    ("Roller seized, belt tracking off to the operator side.", "high"),
    ("Photo eye keeps false triggering when wet.", "normal"),
    ("Bearing housing bolts backed out, unit moving under load.", "critical"),
    ("Belt scraper worn past the wear line.", "low"),
    ("Motor overload tripped twice on start-up this shift.", "high"),
    ("Tail pulley lagging is coming away.", "normal"),
    ("Hydraulic hose chafing on the frame, will burst soon.", "high"),
]

# A realistic spread: most work gets done, some is still queued.
LIFECYCLE = [
    (TicketStatus.closed, 6),
    (TicketStatus.in_progress, 2),
    (TicketStatus.scheduled, 2),
    (TicketStatus.triaged, 2),
    (TicketStatus.reported, 3),
]


def _already_seeded(db) -> bool:
    return (
        db.scalar(
            select(TicketEvent.id).where(TicketEvent.kind == "demo_seed").limit(1)
        )
        is not None
    )


def main() -> int:
    # Fixed seed: re-running produces the same demo set rather than a new
    # random one, so screenshots and walkthroughs stay stable.
    rng = random.Random(20260730)

    with SessionLocal() as db:
        if _already_seeded(db):
            print("demo data already present - nothing to do")
            return 0

        assets = list(db.scalars(select(Asset).order_by(Asset.id)))
        parts = list(db.scalars(select(Part).order_by(Part.id)))
        techs = list(db.scalars(select(Technician).where(Technician.role == "tech")))
        planners = list(
            db.scalars(select(Technician).where(Technician.role == "planner"))
        )
        if not (assets and parts and techs and planners):
            print("reference data missing - run scripts.seed first")
            return 1

        statuses = [status for status, count in LIFECYCLE for _ in range(count)]
        now = datetime.now(UTC)
        created = 0

        for index, (status, (description, priority)) in enumerate(
            zip(statuses, SCENARIOS, strict=False)
        ):
            reporter = rng.choice(techs)
            planner = rng.choice(planners)
            asset = rng.choice(assets)
            reported_at = now - timedelta(
                days=len(statuses) - index, hours=rng.randint(0, 9)
            )

            ticket = Ticket(
                number=next_ticket_number(db),
                asset_id=asset.id,
                description=description,
                status=status,
                priority=TicketPriority(priority),
                reported_by_id=reporter.id,
                reported_at=reported_at,
            )
            db.add(ticket)
            db.flush()

            events: list[tuple[str, str | None, int, datetime]] = [
                (
                    "demo_seed",
                    "Generated sample data - not a real reported fault.",
                    planner.id,
                    reported_at,
                ),
                ("reported", None, reporter.id, reported_at),
            ]
            cursor = reported_at

            if status is not TicketStatus.reported:
                cursor += timedelta(hours=rng.randint(1, 6))
                events.append(
                    ("status_changed", "reported -> triaged", planner.id, cursor)
                )

            if status in (
                TicketStatus.scheduled,
                TicketStatus.in_progress,
                TicketStatus.closed,
            ):
                cursor += timedelta(hours=rng.randint(2, 20))
                assignee = rng.choice(techs)
                ticket.assigned_to_id = assignee.id
                ticket.scheduled_for = cursor + timedelta(days=rng.randint(1, 4))
                events.append(("assigned", assignee.name, planner.id, cursor))
                events.append(
                    (
                        "scheduled",
                        ticket.scheduled_for.strftime("%Y-%m-%d %H:%M"),
                        planner.id,
                        cursor,
                    )
                )

            if status in (TicketStatus.in_progress, TicketStatus.closed):
                cursor += timedelta(hours=rng.randint(4, 30))
                events.append(
                    ("status_changed", "scheduled -> in_progress", reporter.id, cursor)
                )

                for _ in range(rng.randint(1, 3)):
                    part = rng.choice(parts)
                    quantity = rng.choice([1, 2, 4, 6, 10])
                    db.add(
                        PartUsage(
                            ticket_id=ticket.id,
                            part_id=part.id,
                            quantity=quantity,
                            direction=UsageDirection.used,
                            actor_id=reporter.id,
                            at=cursor,
                        )
                    )
                    events.append(
                        (
                            "part_used",
                            f"{quantity} x {part.part_number} {part.part_name}",
                            reporter.id,
                            cursor,
                        )
                    )

            if status is TicketStatus.closed:
                cursor += timedelta(hours=rng.randint(1, 8))
                ticket.closed_at = cursor
                events.append(
                    ("status_changed", "in_progress -> closed", reporter.id, cursor)
                )

            for kind, detail, actor_id, at in events:
                db.add(
                    TicketEvent(
                        ticket_id=ticket.id,
                        kind=kind,
                        detail=detail,
                        actor_id=actor_id,
                        at=at,
                    )
                )
            created += 1

        db.commit()

    print(f"seeded {created} demo tickets (each carries a demo_seed audit event)")
    return 0


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