"""The briefing-package export — the other half of the file contract (D6).

One click produces a zip a Claude session can be handed with zero other
context: the project brief, every material, the links, and an instructions
file that embeds the full import contract and the capture-tool usage. The
receiving session does the thinking; Scout stays deterministic.
"""

import zipfile
from datetime import UTC, datetime
from io import BytesIO

from sqlalchemy.orm import Session

from app.constants import IMPORT_SCHEMA_VERSION, MaterialKind
from app.models.project import Project
from app.services import materials as materials_service
from app.services import storage

# Written as if it will be run verbatim by a fresh session with no context —
# because it will be. Placeholders in braces are filled per-project below.
_INSTRUCTIONS_TEMPLATE = """\
# Instructions for the research session (read fully before starting)

You are a Claude session helping scout website design directions for a client.
This package was exported from Scout (an internal review tool) for the project
**{project_name}** (slug: `{project_slug}`). Your deliverable is a single zip
file — the "research package" — that Scout will import. The people who will
react to your work are the client's stakeholders, on their phones, rating each
option in a few minutes. Choose accordingly.

## What is in this package

- `BRIEF.md` — the project fields and every intake note, including the client's
  own words about what they want. This is your requirements source.
- `materials/` — files the client provided (email threads, documents, images).
- `links.md` — reference links, including the client's current site.

## Step 1 — distill the direction

Read everything. Write (for your own use) a short distillation of the client's
stated direction, quoting their exact words for every claim. Separate:
- what they SAID they want (quote it),
- what they said they do NOT want (quote it),
- what they left open (these are the axes you will diversify on).

## Step 2 — find candidate sites

Find **5–6 live websites** in or near the client's industry that fit. Selection
criteria, in order:

1. **Matches the client's stated vision** — every option must plausibly answer
   "you said X; this is X done well."
2. **Diversifies where the vision is ambiguous** — on the axes the client left
   open (colour temperature, density, personality, imagery use), options should
   deliberately differ, so their reactions resolve the ambiguity.
3. **Avoids features outside project scope** — never show a site whose appeal
   depends on something this project will not build (elaborate portals,
   custom animation showpieces, content operations the client won't run).
   The client would be reacting to something they cannot have.

Prefer sites that capture cleanly (no aggressive cookie walls, no
scroll-hijacking). If a site will not screenshot well, swap it for one that
does — every option needs clean captures.

## Step 3 — capture screenshots

Each option needs TWO full-page PNG captures:
- `screenshots/<slug>-desktop.png` — 1440x900 viewport, full page
- `screenshots/<slug>-mobile.png` — 390x844 viewport, full page

If you are running on the Scout host, the capture tool at
`/srv/apps/scout/tools/capture/run.sh` does this (Playwright in Docker, no
installs): write a JSON file `[{{"slug": "...", "url": "https://..."}}]` and run
`bash run.sh sites.json output-dir/`. Otherwise use any equivalent tool with
the same viewports and full-page setting.

## Step 4 — assemble the research package

A single zip with this exact layout (manifest at the root, no wrapper folder):

```
manifest.json
screenshots/<slug>-desktop.png
screenshots/<slug>-mobile.png
```

`manifest.json` must be:

```json
{{
  "schema_version": {schema_version},
  "project_slug": "{project_slug}",
  "options": [
    {{
      "slug": "kebab-case-stable-id",
      "display_label": "Option A",
      "descriptor": "Confident & structured",
      "source_name": "Firm Name LLP",
      "source_url": "https://…",
      "why_selected": "admin-facing: which client criteria this hits",
      "design_notes": "admin-facing: what to look at on this site",
      "suggested_aspects": ["layout", "navigation"],
      "screenshots": {{"desktop": "screenshots/<slug>-desktop.png",
                       "mobile": "screenshots/<slug>-mobile.png"}}
    }}
  ]
}}
```

Rules the importer enforces (violations reject the whole package):
- `schema_version` must be exactly {schema_version}.
- `project_slug` must be exactly `{project_slug}`.
- Every slug: kebab-case (lowercase letters, digits, hyphens), unique.
- Every option: BOTH screenshots present in the zip at the declared paths.
- No absolute paths or `..` in zip entry names; max 25 MB per file.

Content guidance:
- `display_label`: "Option A" … in your intended viewing order.
- `descriptor`: a neutral 2–5 word feel ("Warm & personal"), never the firm
  name — clients react to the anonymized option, not the brand (that is the
  point of the tool).
- `why_selected`: which of the client's stated criteria this option hits, with
  their quoted words where possible. The admin reads this when deciding what to
  publish.
- `design_notes`: what the designer should look at (the header treatment, the
  practice-area layout, the type pairing…).
- `suggested_aspects` (optional): which of
  `colours, typography, imagery, layout, personality, navigation`
  this option most usefully tests.

Re-importing the same package is safe: options are matched by slug and updated
in place; already-collected reviews are never touched.

## Step 5 — hand it back

Give the zip to the person who exported this package. They upload it in Scout
(project → Options → Import research package), review your draft options, and
publish. Do not publish, email, or share anything yourself.
"""


def instructions_markdown(project: Project) -> str:
    return _INSTRUCTIONS_TEMPLATE.format(
        project_name=project.name,
        project_slug=project.slug,
        schema_version=IMPORT_SCHEMA_VERSION,
    )


def build_briefing_zip(db: Session, project: Project) -> bytes:
    """Assemble the briefing package in memory. Materials are read from disk;
    a file missing on disk is skipped with a line in BRIEF.md rather than
    failing the whole export."""
    items = materials_service.list_for_project(db, project.id)
    notes = [m for m in items if m.kind == MaterialKind.NOTE]
    files = [m for m in items if m.kind == MaterialKind.FILE]
    links = [m for m in items if m.kind == MaterialKind.LINK]

    exported = datetime.now(UTC).strftime("%Y-%m-%d %H:%M UTC")
    brief_lines = [
        f"# Briefing — {project.name}",
        "",
        f"*Exported from Scout {exported}.*",
        "",
        f"- **Client:** {project.client_name or '—'}",
        f"- **Current website:** {project.client_website or '—'}",
        f"- **Project slug (for the manifest):** `{project.slug}`",
        "",
    ]
    if project.brief:
        brief_lines += ["## Project brief", "", project.brief, ""]
    if notes:
        brief_lines.append("## Intake notes")
        brief_lines.append("")
        for note in notes:
            title = note.title or "Note"
            brief_lines += [f"### {title}", "", note.body, ""]
    if files:
        brief_lines.append("## Files in materials/")
        brief_lines.append("")

    link_lines = ["# Links", ""]
    if project.client_website:
        link_lines.append(f"- Current site: {project.client_website}")
    for link in links:
        label = link.title or link.url
        link_lines.append(f"- {label}: {link.url}")
    if len(link_lines) == 2:
        link_lines.append("_No links recorded._")

    buffer = BytesIO()
    with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as archive:
        for material in files:
            arcname = f"materials/{material.id}-{material.file_name}"
            try:
                content = storage.resolve(material.file_path).read_bytes()
            except (OSError, storage.StorageError):
                brief_lines.append(f"- MISSING ON DISK: {material.file_name}")
                continue
            archive.writestr(arcname, content)
            brief_lines.append(f"- `{arcname}` — {material.title or material.file_name}")

        archive.writestr("BRIEF.md", "\n".join(brief_lines) + "\n")
        archive.writestr("links.md", "\n".join(link_lines) + "\n")
        archive.writestr("INSTRUCTIONS-FOR-CLAUDE.md", instructions_markdown(project))

    return buffer.getvalue()
