"""Hello-world app code for APX-scaffolded apps.

Writes a sample 3-layer API endpoint + React page that queries the
sample customer data and displays it in a shadcn Table. Proves the
app-to-lakehouse data flow end-to-end. Claude replaces these during
/plan + /build-next Wave 4.
"""

from __future__ import annotations

import logging
from pathlib import Path

from exokit.core.models import ScaffoldContext

logger = logging.getLogger(__name__)

# ---------------------------------------------------------------------------
# Backend: schema
# ---------------------------------------------------------------------------

_SCHEMA_PY = '''\
"""Sample schema — replaced by /plan + /build-next."""

from __future__ import annotations

from pydantic import BaseModel


class CustomerRow(BaseModel):
    """A single customer from silver_customers."""

    customer_id: str | None = None
    name: str | None = None
    email: str | None = None
    age: int | None = None
    balance: float | None = None
    risk_score: float | None = None
    signup_date: str | None = None


class CustomersListOut(BaseModel):
    """Paginated customer list."""

    customers: list[CustomerRow] = []
    total: int = 0


class SummaryOut(BaseModel):
    """Summary metrics from gold_customer_summary."""

    total_customers: int | None = None
    avg_balance: float | None = None
    avg_risk_score: float | None = None
    avg_age: float | None = None
'''

# ---------------------------------------------------------------------------
# Backend: repository
# ---------------------------------------------------------------------------


def _repository_py(catalog: str) -> str:
    return f'''\
"""Sample repository — replaced by /plan + /build-next."""

from __future__ import annotations

import os

from databricks.sdk import WorkspaceClient
from databricks.sdk.service.sql import ExecuteStatementRequestOnWaitTimeout

CATALOG = "{catalog}"
WAREHOUSE_ID = os.environ.get("DATABRICKS_SQL_WAREHOUSE_ID", "")


class SampleRepository:
    """Reads sample data from the lakehouse using the app service principal."""

    def __init__(self) -> None:
        self._ws = WorkspaceClient()

    @staticmethod
    def _rows_to_dicts(result: object) -> list[dict]:
        """Convert Statement Execution API result to list of dicts."""
        if not hasattr(result, "result") or result.result is None:
            return []
        if result.result.data_array is None:
            return []
        cols = [c.name for c in result.manifest.schema.columns]
        return [dict(zip(cols, row, strict=False)) for row in result.result.data_array]

    def _execute(self, statement: str) -> object:
        return self._ws.statement_execution.execute_statement(
            warehouse_id=WAREHOUSE_ID,
            statement=statement,
            wait_timeout="30s",
            on_wait_timeout=ExecuteStatementRequestOnWaitTimeout.CONTINUE,
        )

    def list_customers(self, limit: int = 50) -> tuple[list[dict], int]:
        """Fetch customers from silver_customers."""
        count_result = self._execute(
            f"SELECT COUNT(*) AS cnt FROM {{CATALOG}}.default.silver_customers"
        )
        count_rows = self._rows_to_dicts(count_result)
        total = int(count_rows[0]["cnt"]) if count_rows else 0

        result = self._execute(f"""
            SELECT customer_id, name, email, age, balance, risk_score, signup_date
            FROM {{CATALOG}}.default.silver_customers
            ORDER BY customer_id
            LIMIT {{limit}}
        """)
        return self._rows_to_dicts(result), total

    def get_summary(self) -> dict:
        """Fetch summary from gold_customer_summary."""
        result = self._execute(f"""
            SELECT total_customers, avg_balance, avg_risk_score, avg_age
            FROM {{CATALOG}}.default.gold_customer_summary
            LIMIT 1
        """)
        rows = self._rows_to_dicts(result)
        return rows[0] if rows else {{}}
'''


# ---------------------------------------------------------------------------
# Backend: service
# ---------------------------------------------------------------------------

_SERVICE_PY = '''\
"""Sample service — replaced by /plan + /build-next."""

from __future__ import annotations

from ..repositories.sample import SampleRepository
from ..schemas.sample import CustomerRow, CustomersListOut, SummaryOut


class SampleService:
    """Business logic for the hello-world endpoints."""

    def __init__(self) -> None:
        self._repo = SampleRepository()

    def list_customers(self, limit: int = 50) -> CustomersListOut:
        rows, total = self._repo.list_customers(limit=limit)
        customers = []
        for row in rows:
            cleaned = {k: (None if v == "" else v) for k, v in row.items()}
            customers.append(CustomerRow(**cleaned))
        return CustomersListOut(customers=customers, total=total)

    def get_summary(self) -> SummaryOut:
        row = self._repo.get_summary()
        cleaned = {k: (None if v == "" else v) for k, v in row.items()}
        return SummaryOut(**cleaned)
'''

# ---------------------------------------------------------------------------
# Backend: API router
# ---------------------------------------------------------------------------

_API_PY = '''\
"""Sample API — replaced by /plan + /build-next.

Two endpoints proving app -> lakehouse data flow:
  GET /sample/customers  — table of customers from silver
  GET /sample/summary    — metrics from gold

Uses the app service principal for SQL queries (not OBO).
"""

from __future__ import annotations

from ..core import create_router
from ..schemas.sample import CustomersListOut, SummaryOut
from ..services.sample import SampleService

router = create_router()

_svc = SampleService()


@router.get(
    "/sample/customers",
    response_model=CustomersListOut,
    operation_id="listSampleCustomers",
    tags=["sample"],
)
def list_customers(limit: int = 50) -> CustomersListOut:
    """List sample customers from silver_customers."""
    return _svc.list_customers(limit=limit)


@router.get(
    "/sample/summary",
    response_model=SummaryOut,
    operation_id="getSampleSummary",
    tags=["sample"],
)
def get_summary() -> SummaryOut:
    """Summary metrics from gold_customer_summary."""
    return _svc.get_summary()
'''

# ---------------------------------------------------------------------------
# Frontend: React page with shadcn Table
# ---------------------------------------------------------------------------

_SAMPLE_TSX = """\
import React from "react";
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/sample")({
  component: () => <SamplePage />,
});

interface Customer {
  customer_id: string | null;
  name: string | null;
  email: string | null;
  age: number | null;
  balance: number | null;
  risk_score: number | null;
  signup_date: string | null;
}

interface Summary {
  total_customers: number | null;
  avg_balance: number | null;
  avg_risk_score: number | null;
  avg_age: number | null;
}

/**
 * Hello-world page: queries silver_customers + gold_customer_summary.
 * Uses Tailwind only (no shadcn deps) so it works with APX's default scaffold.
 * Replaced by /plan + /build-next during Wave 4 with proper shadcn components.
 */
function SamplePage() {
  const [customers, setCustomers] = React.useState<Customer[]>([]);
  const [summary, setSummary] = React.useState<Summary>({
    total_customers: null, avg_balance: null,
    avg_risk_score: null, avg_age: null,
  });
  const [total, setTotal] = React.useState(0);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState<string | null>(null);

  React.useEffect(() => {
    Promise.all([
      fetch("/api/sample/customers?limit=20").then((r) => r.json()),
      fetch("/api/sample/summary").then((r) => r.json()),
    ])
      .then(([custData, sumData]) => {
        setCustomers(custData.customers || []);
        setTotal(custData.total || 0);
        setSummary(sumData);
        setLoading(false);
      })
      .catch((err) => {
        setError(err.message || "Failed to load data");
        setLoading(false);
      });
  }, []);

  if (loading) {
    return (
      <div className="p-6 space-y-6">
        <div className="h-8 w-64 bg-muted animate-pulse rounded" />
        <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
          {Array.from({ length: 4 }).map((_, i) => (
            <div key={i} className="h-24 bg-muted animate-pulse rounded-xl" />
          ))}
        </div>
        <div className="h-64 bg-muted animate-pulse rounded-xl" />
      </div>
    );
  }

  if (error) {
    return (
      <div className="p-6">
        <div className="rounded-lg border border-destructive p-6">
          <p className="text-destructive font-medium">Error: {error}</p>
          <p className="text-sm text-muted-foreground mt-2">
            Make sure the lakehouse pipeline has run and data exists in the
            silver/gold tables.
          </p>
        </div>
      </div>
    );
  }

  const kpis = [
    { label: "Total Customers", value: summary.total_customers },
    { label: "Avg Balance", value: summary.avg_balance, prefix: "$" },
    { label: "Avg Risk Score", value: summary.avg_risk_score },
    { label: "Avg Age", value: summary.avg_age },
  ];

  return (
    <div className="p-6 space-y-6">
      <div>
        <h1 className="text-2xl font-bold">Sample Dashboard</h1>
        <p className="text-muted-foreground">
          Hello-world page querying lakehouse data. Replaced during Wave 4.
        </p>
      </div>

      {/* KPI cards */}
      <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
        {kpis.map((kpi) => (
          <div key={kpi.label} className="rounded-lg border bg-card p-5">
            <p className="text-sm text-muted-foreground">{kpi.label}</p>
            <p className="text-2xl font-bold mt-1 tabular-nums">
              {kpi.value != null
                ? `${kpi.prefix ?? ""}${Number(kpi.value).toLocaleString()}`
                : "\\u2014"}
            </p>
          </div>
        ))}
      </div>

      {/* Customers table */}
      <div className="rounded-lg border">
        <div className="p-4 border-b">
          <h2 className="text-sm font-semibold">
            Customers ({total} total)
          </h2>
        </div>
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead>
              <tr className="border-b bg-muted/50">
                <th className="px-4 py-3 text-left font-medium">ID</th>
                <th className="px-4 py-3 text-left font-medium">Name</th>
                <th className="px-4 py-3 text-left font-medium">Email</th>
                <th className="px-4 py-3 text-right font-medium">Balance</th>
                <th className="px-4 py-3 text-right font-medium">Risk</th>
                <th className="px-4 py-3 text-left font-medium">Signup</th>
              </tr>
            </thead>
            <tbody>
              {customers.map((c) => (
                <tr key={c.customer_id} className="border-b hover:bg-muted/50">
                  <td className="px-4 py-3 font-mono text-xs">
                    {c.customer_id}
                  </td>
                  <td className="px-4 py-3 font-medium">{c.name}</td>
                  <td className="px-4 py-3 text-muted-foreground">
                    {c.email}
                  </td>
                  <td className="px-4 py-3 text-right tabular-nums">
                    ${Number(c.balance).toLocaleString()}
                  </td>
                  <td className="px-4 py-3 text-right">
                    <span
                      className={`inline-flex px-2 py-0.5 rounded-full text-xs font-medium ${
                        Number(c.risk_score) > 0.7
                          ? "bg-red-100 text-red-800 dark:bg-red-900/50 dark:text-red-200"
                          : Number(c.risk_score) > 0.4
                            ? "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/50"
                            : "bg-green-100 text-green-800 dark:bg-green-900/50"
                      }`}
                    >
                      {Number(c.risk_score).toFixed(3)}
                    </span>
                  </td>
                  <td className="px-4 py-3 text-muted-foreground">
                    {c.signup_date}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}
"""

# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


def write_hello_world_app(context: ScaffoldContext, app_dir: Path) -> None:
    """Write sample 3-layer API + React page into APX-scaffolded app.

    Creates: schemas/sample.py, repositories/sample.py, services/sample.py,
    api/sample.py, and ui/routes/sample.tsx. Registers the API route in app.py.
    """
    app_slug = context.app_name.replace("-", "_")
    backend = app_dir / "src" / app_slug / "backend"
    ui = app_dir / "src" / app_slug / "ui"

    if not backend.exists():
        logger.warning("Backend not found at %s, skipping hello-world app", backend)
        return

    # Write 3-layer backend
    for subdir, content in [
        ("schemas", _SCHEMA_PY),
        ("repositories", _repository_py(context.catalog)),
        ("services", _SERVICE_PY),
        ("api", _API_PY),
    ]:
        target_dir = backend / subdir
        target_dir.mkdir(parents=True, exist_ok=True)
        target_file = target_dir / "sample.py"
        if not target_file.exists():
            target_file.write_text(content, encoding="utf-8")

    # Register route in app.py
    app_py = backend / "app.py"
    if app_py.exists():
        app_content = app_py.read_text(encoding="utf-8")
        if "api.sample" not in app_content:
            import_line = "from .api import sample as _sample  # noqa: F401\n"
            if "app = create_app(" in app_content:
                app_content = app_content.replace(
                    "app = create_app(",
                    f"{import_line}\napp = create_app(",
                    1,
                )
                app_py.write_text(app_content, encoding="utf-8")
                logger.info("Registered sample API route in app.py")

    # Write React page
    routes_dir = ui / "routes"
    if routes_dir.exists():
        sample_tsx = routes_dir / "sample.tsx"
        if not sample_tsx.exists():
            sample_tsx.write_text(_SAMPLE_TSX, encoding="utf-8")
            logger.info("Wrote sample React page")

        # Replace the default APX index page with a redirect to /sample
        index_tsx = routes_dir / "index.tsx"
        if index_tsx.exists():
            redirect_code = (
                "import { Navigate, createFileRoute }"
                ' from "@tanstack/react-router";\n\n'
                "export const Route = createFileRoute"
                '("/")({ component: () =>'
                ' <Navigate to="/sample" /> });\n'
            )
            index_tsx.write_text(redirect_code, encoding="utf-8")
            logger.info("Replaced index.tsx with redirect to /sample")

    logger.info("Hello-world app code written (3-layer backend + React page)")
