"""The Scouting Report — the deliverable of the whole loop (locked decision D16).

Assembled server-side as markdown from the same rollup the results screen
reads. This is the admin/designer-facing artifact: sources are revealed here,
and consensus AND splits are both presented — disagreement between stakeholders
is signal, not noise (D7).
"""

from datetime import UTC, datetime

from sqlalchemy.orm import Session

from app.constants import ASPECT_LABELS, RATING_LABELS
from app.models.project import Project
from app.services import options as options_service
from app.services import reviews as reviews_service

_FOOTER = """\
---

## How to use this report (for the next Claude session)

This is the output of a Scout review round: each stakeholder independently rated
anonymized design-direction options ("Option A/B/C…") on a 0–3 gut scale, voted
thumbs-up/thumbs-down on fixed aspects, and named the option closest to how
their site should feel. Sources are revealed above; the clients never saw them.

To turn this into a design direction:

1. Read the "Direction (designer's synthesis)" section first — if the designer
   has filled it in, it overrides any inference you draw yourself.
2. Weight the FINAL PICKS and ratings of 3 ("Love it") most heavily; treat 0
   ("Not for us") as a hard exclusion signal for that option's overall feel.
3. Use the per-aspect tallies to separate WHAT people liked from WHICH option
   they liked — a losing option can still win an aspect (e.g. its typography).
4. Read every verbatim note; quote the client's own words in the design brief.
5. Where stakeholders split, present the split to the designer as an open
   question — do not average it away.

Produce: a one-page design direction (palette temperature, type personality,
layout density, imagery approach, navigation structure), each point traceable to
a rating, a tally, or a quote above.
"""


def _rating_word(rating: int | None) -> str:
    if rating is None:
        return "(no rating)"
    return f"{rating} — {RATING_LABELS.get(str(rating), '?')}"


def build_report(db: Session, project: Project) -> str:
    data = reviews_service.rollup(db, project.id)
    accounts, is_client = data["accounts"], data["is_client"]
    reviews, votes, picks = data["reviews"], data["votes"], data["picks"]
    options = options_service.list_for_admin(db, project.id)

    def name_of(username: str) -> str:
        account = accounts.get(username)
        label = account.display_name if account is not None else username
        return label if is_client(username) else f"{label} (staff preview)"

    lines: list[str] = []
    add = lines.append

    add(f"# Scouting Report — {project.name}")
    add("")
    generated = datetime.now(UTC).strftime("%Y-%m-%d")
    add(f"*Generated {generated} by Scout. Admin/designer-facing: sources are revealed below.*")
    add("")
    add("## Direction (designer's synthesis)")
    add("")
    add("_To be filled in by the designer after reading the results below._")
    add("")
    add("## Project")
    add("")
    add(f"- **Client:** {project.client_name or '—'}")
    add(f"- **Current site:** {project.client_website or '—'}")
    if project.brief:
        add(f"- **Brief:** {project.brief}")
    add("")

    reviews_by_option: dict[int, list] = {}
    for review in reviews:
        reviews_by_option.setdefault(review.option_id, []).append(review)
    votes_by_option: dict[int, list] = {}
    for vote in votes:
        votes_by_option.setdefault(vote.option_id, []).append(vote)
    picks_by_option: dict[int, int] = {}
    for pick in picks:
        if pick.option_id is not None and is_client(pick.username):
            picks_by_option[pick.option_id] = picks_by_option.get(pick.option_id, 0) + 1

    add("## Options")
    add("")
    for option in options:
        add(f"### {option.display_label} — {option.source_name or option.slug}")
        add("")
        if option.descriptor:
            add(f"*{option.descriptor}*")
            add("")
        if option.source_url:
            add(f"- **Source:** {option.source_url}")
        add(f"- **Status:** {option.status}")
        if option.why_selected:
            add(f"- **Why it was selected:** {option.why_selected}")
        if option.design_notes:
            add(f"- **Design notes:** {option.design_notes}")

        client_ratings = [
            r for r in reviews_by_option.get(option.id, []) if is_client(r.username)
        ]
        rated = [r for r in client_ratings if r.rating is not None]
        if rated:
            mean = sum(r.rating for r in rated) / len(rated)
            add(f"- **Client ratings ({len(rated)}):** mean {mean:.1f}")
            for review in sorted(rated, key=lambda r: -r.rating):
                add(f"  - {name_of(review.username)}: {_rating_word(review.rating)}")
        else:
            add("- **Client ratings:** none yet")
        admin_rated = [
            r
            for r in reviews_by_option.get(option.id, [])
            if not is_client(r.username) and r.rating is not None
        ]
        for review in admin_rated:
            add(f"  - {name_of(review.username)}: {_rating_word(review.rating)}")

        option_votes = [v for v in votes_by_option.get(option.id, []) if is_client(v.username)]
        if option_votes:
            tallies: dict[str, list[int]] = {}
            for vote in option_votes:
                bucket = tallies.setdefault(vote.aspect, [0, 0])
                bucket[0 if vote.vote > 0 else 1] += 1
            parts = [
                f"{ASPECT_LABELS.get(aspect, aspect)} +{up}/-{down}"
                for aspect, (up, down) in sorted(tallies.items())
            ]
            add(f"- **Aspect votes:** {' · '.join(parts)}")

        if picks_by_option.get(option.id):
            add(f"- **Final pick by {picks_by_option[option.id]} client reviewer(s)**")

        notes = [r for r in reviews_by_option.get(option.id, []) if r.note]
        if notes:
            add("- **Notes:**")
            for review in notes:
                add(f"  - {name_of(review.username)}: “{review.note}”")
        add("")

    add("## The aspect story across options")
    add("")
    aspect_totals: dict[str, list[int]] = {aspect: [0, 0] for aspect in ASPECT_LABELS}
    for vote in votes:
        if is_client(vote.username):
            aspect_totals[vote.aspect][0 if vote.vote > 0 else 1] += 1
    any_votes = False
    for aspect, (up, down) in aspect_totals.items():
        if up or down:
            any_votes = True
            add(f"- **{ASPECT_LABELS[aspect]}:** {up} up / {down} down (net {up - down:+d})")
    if not any_votes:
        add("_No aspect votes collected yet._")
    add("")

    add("## Final picks")
    add("")
    option_labels = {option.id: option.display_label for option in options}
    if picks:
        for pick in picks:
            label = option_labels.get(pick.option_id, "—") if pick.option_id else "—"
            state = "finished" if pick.completed_at else "in progress"
            add(f"- **{name_of(pick.username)}** picked **{label}** ({state})")
            if pick.closing_note:
                add(f"  - “{pick.closing_note}”")
    else:
        add("_No final picks yet._")
    add("")

    add(_FOOTER)
    return "\n".join(lines)
