"""PDF генератор для отчёта Trade-In Estimator (TI-2).

Структура отчёта — 4 страницы (как у Брусника.Обмен):
  1. Cover    — шапка + адрес + параметры квартиры + hero-цена
  2. Listings — таблица объявлений-аналогов (top 10)
  3. Deals    — таблица фактических сделок (last 12 мес.)
  4. Offer    — выкупная стоимость trade-in (placeholder Phase 2)

Pattern: backend/app/services/exporters/layout_tz_pdf.py (WeasyPrint, уже работает).
"""

from __future__ import annotations

import datetime as dt
import html as _html
import logging

from app.schemas.trade_in import AggregatedEstimate, AnalogLot

logger = logging.getLogger(__name__)

# ── helpers ──────────────────────────────────────────────────────────────────


def _fmt_rub(value: int) -> str:
    """Форматирование рублей с пробелами-разделителями: 12 500 000 ₽"""
    return f"{value:,}".replace(",", " ") + " ₽"


def _fmt_rub_m(value: int) -> str:
    """Форматирование в миллионах: 12,50 млн ₽"""
    m = value / 1_000_000
    return f"{m:.2f}".replace(".", ",") + " млн ₽"


def _fmt_ppm2(value: int) -> str:
    return f"{value:,}".replace(",", " ") + " ₽/м²"


def _conf_color(confidence: str) -> tuple[str, str, str]:
    """(bg, fg, border) для badge уверенности."""
    mapping = {
        "high": ("#dcfce7", "#15803d", "#86efac"),
        "medium": ("#fef9c3", "#a16207", "#fde68a"),
        "low": ("#fee2e2", "#b91c1c", "#fca5a5"),
    }
    return mapping.get(confidence, ("#f3f4f6", "#374151", "#d1d5db"))


def _conf_label(confidence: str) -> str:
    return {"high": "Высокая", "medium": "Средняя", "low": "Низкая"}.get(confidence, confidence)


def _analog_rows(lots: list[AnalogLot], *, is_deal: bool) -> str:
    if not lots:
        return "<tr><td colspan='6' class='empty'>Нет данных</td></tr>"
    rows = []
    for lot in lots:
        date_val = lot.listing_date.strftime("%d.%m.%Y") if lot.listing_date else "—"
        dom_val = str(lot.days_on_market) if lot.days_on_market is not None else "—"
        floor_val = (
            f"{lot.floor}/{lot.total_floors}"
            if lot.floor is not None and lot.total_floors is not None
            else "—"
        )
        label = "Дата сделки" if is_deal else "В продаже"
        _ = label  # used for header only
        rows.append(
            "<tr>"
            f"<td>{_html.escape(lot.address)}</td>"
            f"<td class='num'>{lot.area_m2:.1f}</td>"
            f"<td class='num'>{lot.rooms if lot.rooms else 'С'}</td>"
            f"<td class='num'>{floor_val}</td>"
            f"<td class='num'>{_fmt_rub(lot.price_rub)}</td>"
            f"<td class='num'>{date_val} ({dom_val}д.)</td>"
            "</tr>"
        )
    return "".join(rows)


# ── CSS ──────────────────────────────────────────────────────────────────────


def _build_css() -> str:
    return """
@page {
    size: A4;
    margin: 20mm 18mm 20mm 18mm;
}
* { box-sizing: border-box; }
body {
    font-family: 'Helvetica', 'Arial', sans-serif;
    font-size: 10pt;
    color: #1a1d23;
    margin: 0;
    padding: 0;
}
h1 { font-size: 18pt; margin: 0 0 4pt 0; line-height: 1.2; }
h2 { font-size: 13pt; margin: 0 0 8pt 0; border-bottom: 1.5px solid #e6e8ec; padding-bottom: 4pt; }
h3 { font-size: 11pt; margin: 0 0 6pt 0; color: #374151; }
.page { page-break-after: always; }
.page:last-child { page-break-after: avoid; }

/* Cover */
.cover-header {
    background: #1d4ed8; color: #fff;
    padding: 20pt; border-radius: 6pt; margin-bottom: 16pt;
}
.cover-header h1 { color: #fff; }
.cover-meta { font-size: 9pt; color: rgba(255,255,255,0.8); margin-top: 4pt; }
.hero-price {
    font-size: 28pt; font-weight: 800;
    font-variant-numeric: tabular-nums; margin: 12pt 0 4pt;
}
.hero-ppm2 { font-size: 12pt; color: #5b6066; font-variant-numeric: tabular-nums; }
.range-bar-wrap { margin: 10pt 0; }
.range-label { font-size: 9pt; color: #9ca3af; }
.badge {
    display: inline-block; padding: 4pt 10pt;
    border-radius: 6pt; font-size: 9pt; font-weight: 700; margin-top: 8pt;
}
.params-grid {
    display: grid; grid-template-columns: 1fr 1fr 1fr;
    gap: 8pt; margin-top: 10pt;
}
.param-item .param-label {
    font-size: 8pt; color: #9ca3af;
    text-transform: uppercase; letter-spacing: 0.03em;
}
.param-item .param-value { font-size: 10pt; font-weight: 600; }

/* Tables */
table { width: 100%; border-collapse: collapse; margin-top: 8pt; font-size: 9pt; }
thead tr { background: #f3f4f6; }
th {
    padding: 5pt 7pt; text-align: left; font-weight: 700;
    color: #374151; border-bottom: 1.5px solid #d1d5db;
}
td { padding: 5pt 7pt; border-bottom: 1px solid #e6e8ec; vertical-align: top; }
td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
tr:nth-child(even) td { background: #f9fafb; }
td.empty { text-align: center; color: #9ca3af; padding: 14pt; }

/* Offer page */
.offer-table { margin-top: 14pt; }
.offer-table td { font-size: 10pt; }
.offer-total td { font-weight: 700; background: #eff6ff !important; }
.advantages { display: grid; grid-template-columns: 1fr 1fr; gap: 10pt; margin-top: 16pt; }
.advantage-item { border: 1px solid #e6e8ec; border-radius: 6pt; padding: 10pt; }
.advantage-item .adv-title { font-weight: 700; font-size: 10pt; margin-bottom: 4pt; }
.advantage-item .adv-desc { font-size: 9pt; color: #5b6066; }

/* Footer */
.footer {
    margin-top: 20pt; padding-top: 8pt;
    border-top: 1px solid #e6e8ec; font-size: 8pt; color: #9ca3af;
}
.source-logos { color: #6b7280; font-size: 8pt; margin-top: 4pt; }
"""


# ── HTML builder ─────────────────────────────────────────────────────────────


def _build_html(estimate: AggregatedEstimate, input_snapshot: dict) -> str:  # type: ignore[type-arg]
    today = dt.date.today().strftime("%d.%m.%Y")
    address = _html.escape(input_snapshot.get("address", "—"))
    area_m2: float = input_snapshot.get("area_m2", 0)
    rooms: int = input_snapshot.get("rooms", 0)
    floor: int = input_snapshot.get("floor", 0)
    total_floors: int = input_snapshot.get("total_floors", 0)
    year_built = input_snapshot.get("year_built")
    house_type = input_snapshot.get("house_type")
    repair_state = input_snapshot.get("repair_state")
    has_balcony = input_snapshot.get("has_balcony")

    conf_bg, conf_fg, conf_border = _conf_color(estimate.confidence)
    conf_label = _conf_label(estimate.confidence)

    rooms_label = "Студия" if rooms == 0 else f"{rooms}-комн."
    house_labels = {
        "panel": "Панель",
        "brick": "Кирпич",
        "monolith": "Монолит",
        "monolith_brick": "Монолит-кирпич",
        "other": "Другое",
    }
    repair_labels = {
        "needs_repair": "Требует ремонта",
        "standard": "Стандартный",
        "good": "Хороший",
        "excellent": "Евроремонт",
    }

    # Cover params grid items
    params = [
        ("Площадь", f"{area_m2} м²"),
        ("Комнат", rooms_label),
        ("Этаж", f"{floor} из {total_floors}"),
    ]
    if year_built:
        params.append(("Год постройки", str(year_built)))
    if house_type:
        params.append(("Тип дома", house_labels.get(house_type, house_type)))
    if repair_state:
        params.append(("Ремонт", repair_labels.get(repair_state, repair_state)))
    if has_balcony is not None:
        params.append(("Балкон", "Есть" if has_balcony else "Нет"))

    params_html = "".join(
        f"""<div class="param-item">
          <div class="param-label">{k}</div>
          <div class="param-value">{_html.escape(str(v))}</div>
        </div>"""
        for k, v in params
    )

    listing_rows = _analog_rows(estimate.analogs, is_deal=False)
    deal_rows = _analog_rows(estimate.actual_deals, is_deal=True)

    # Trade-in cost table (stub: -7% buyout)
    median = estimate.median_price_rub
    torg_pct = 7
    buyout_pct = 5  # trade-in discount on top
    torg = int(median * torg_pct / 100)
    buyout_discount = int(median * buyout_pct / 100)
    tradein_price = median - torg - buyout_discount

    return f"""<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Trade-In Оценка — {address}</title>
</head>
<body>

<!-- ═══════════════════════════ PAGE 1 — COVER ═══════════════════════════ -->
<div class="page">
  <div class="cover-header">
    <h1>Оценка квартиры — Trade-In</h1>
    <div class="cover-meta">{address}</div>
    <div class="cover-meta">Сформировано: {today} · Действительно 24 часа</div>
  </div>

  <div style="display:flex; align-items:flex-start; justify-content:space-between; gap:16pt;">
    <div>
      <div style="font-size:9pt; color:#5b6066; text-transform:uppercase; letter-spacing:0.05em;">
        Оценочная стоимость
      </div>
      <div class="hero-price">{_fmt_rub_m(estimate.median_price_rub)}</div>
      <div class="hero-ppm2">{_fmt_ppm2(estimate.median_price_per_m2)}</div>
      <div class="range-label" style="margin-top:8pt;">
        Диапазон: {_fmt_rub_m(estimate.range_low_rub)} — {_fmt_rub_m(estimate.range_high_rub)}
        · данные за {estimate.period_months} мес.
      </div>
    </div>
    <div class="badge"
         style="background:{conf_bg}; color:{conf_fg}; border:1px solid {conf_border};">
      Достоверность: {conf_label}<br>
      <span style="font-size:8pt; font-weight:400;">{estimate.n_analogs} аналогов</span>
    </div>
  </div>

  <h3 style="margin-top:16pt;">Параметры объекта</h3>
  <div class="params-grid">
    {params_html}
  </div>

  <div class="footer">
    <div>GenDesign · Trade-In Estimator · mock-данные (Phase 1 MVP)</div>
    <div class="source-logos">Источники данных: Циан · Авито · ДомКлик · Росреестр</div>
  </div>
</div>

<!-- ═══════════════════════════ PAGE 2 — LISTINGS ════════════════════════ -->
<div class="page">
  <h2>Объявления-аналоги ({len(estimate.analogs)})</h2>
  <p style="font-size:9pt; color:#6b7280; margin-top:0;">
    Аналоги из открытых источников (Циан, Авито, ДомКлик) — схожая комнатность,
    площадь ±15%, тот же район. Используются как ориентир рыночной цены.
  </p>
  <table>
    <thead>
      <tr>
        <th>Адрес</th>
        <th>Пл., м²</th>
        <th>Комн.</th>
        <th>Этаж</th>
        <th>Цена</th>
        <th>Дата / Экспозиция</th>
      </tr>
    </thead>
    <tbody>
      {listing_rows}
    </tbody>
  </table>

  <div class="footer">
    <div>GenDesign · Trade-In Estimator · стр. 2</div>
  </div>
</div>

<!-- ═══════════════════════════ PAGE 3 — DEALS ═══════════════════════════ -->
<div class="page">
  <h2>Фактические сделки ({len(estimate.actual_deals)})</h2>
  <p style="font-size:9pt; color:#6b7280; margin-top:0;">
    Сделки из Росреестра (ДДУ + переуступка) за последние 12 месяцев — отражают
    реальные цены покупки, в отличие от цен предложения.
  </p>
  <table>
    <thead>
      <tr>
        <th>Адрес</th>
        <th>Пл., м²</th>
        <th>Комн.</th>
        <th>Этаж</th>
        <th>Цена сделки</th>
        <th>Дата / Срок</th>
      </tr>
    </thead>
    <tbody>
      {deal_rows}
    </tbody>
  </table>

  <div class="footer">
    <div>GenDesign · Trade-In Estimator · стр. 3</div>
  </div>
</div>

<!-- ═══════════════════════════ PAGE 4 — TRADE-IN COST ══════════════════ -->
<div class="page">
  <h2>Выкупная стоимость (trade-in)</h2>
  <p style="font-size:9pt; color:#6b7280; margin-top:0;">
    Ориентировочный расчёт. Финальная цена выкупа согласовывается с менеджером
    и зависит от конкретного объекта и условий сделки.
  </p>

  <table class="offer-table">
    <tbody>
      <tr>
        <td>Рыночная стоимость (медиана)</td>
        <td class="num">{_fmt_rub(median)}</td>
      </tr>
      <tr>
        <td>— Торговый дисконт ({torg_pct}%)</td>
        <td class="num">−{_fmt_rub(torg)}</td>
      </tr>
      <tr>
        <td>— Дисконт за срочность выкупа ({buyout_pct}%)</td>
        <td class="num">−{_fmt_rub(buyout_discount)}</td>
      </tr>
      <tr class="offer-total">
        <td>Выкупная цена (ориентир)</td>
        <td class="num">{_fmt_rub(tradein_price)}</td>
      </tr>
    </tbody>
  </table>

  <div style="margin-top:14pt; padding:10pt; background:#eff6ff; border-left:3px solid #1d4ed8;
              border-radius:0 4pt 4pt 0; font-size:9pt; color:#1e40af;">
    <strong>Важно:</strong> данные получены из mock-источников (MVP Phase 1).
    В Phase 2 расчёт будет основан на реальных данных Циан/Авито/Росреестр
    для конкретного адреса и подтверждён менеджером.
  </div>

  <h3 style="margin-top:18pt;">4 преимущества trade-in</h3>
  <div class="advantages">
    <div class="advantage-item">
      <div class="adv-title">Скорость</div>
      <div class="adv-desc">Сделка за 2–4 недели вместо 3–6 месяцев самостоятельной продажи</div>
    </div>
    <div class="advantage-item">
      <div class="adv-title">Без хлопот</div>
      <div class="adv-desc">Показы, торг, документы — всё берёт на себя девелопер</div>
    </div>
    <div class="advantage-item">
      <div class="adv-title">Зачёт в счёт новостройки</div>
      <div class="adv-desc">Стоимость квартиры напрямую идёт в оплату нового жилья</div>
    </div>
    <div class="advantage-item">
      <div class="adv-title">Фиксация цены</div>
      <div class="adv-desc">Цена новостройки фиксируется на момент подачи заявки</div>
    </div>
  </div>

  <div class="footer">
    <div>GenDesign · Trade-In Estimator · стр. 4</div>
    <div class="source-logos">
      Расчёт носит ориентировочный характер и не является офертой.
    </div>
  </div>
</div>

</body>
</html>"""


# ── public API ────────────────────────────────────────────────────────────────


def generate_trade_in_pdf(estimate: AggregatedEstimate, input_snapshot: dict) -> bytes:  # type: ignore[type-arg]
    """Генерирует 4-страничный WeasyPrint PDF для Trade-In оценки.

    Pages:
      1. Cover    — шапка + адрес + параметры + hero-цена
      2. Listings — таблица объявлений-аналогов (top 10)
      3. Deals    — таблица фактических сделок (last 12 мес.)
      4. Offer    — выкупная стоимость trade-in + 4 преимущества

    Args:
        estimate: AggregatedEstimate из БД
        input_snapshot: словарь с полями ввода пользователя (address, area_m2, ...)

    Returns:
        PDF bytes готовые для Response(media_type="application/pdf")
    """
    # WeasyPrint импортируем локально — тяжёлый; не нужен при импорте модуля
    # (иначе ломает сбор pytest на хостах без native-libs, напр. macOS).
    from weasyprint import CSS, HTML

    html_str = _build_html(estimate, input_snapshot)
    css_str = _build_css()
    pdf_bytes = HTML(string=html_str).write_pdf(stylesheets=[CSS(string=css_str)])
    logger.info(
        "Generated trade-in PDF estimate_id=%s pages=4 size=%d bytes",
        estimate.estimate_id,
        len(pdf_bytes),
    )
    return pdf_bytes
