"""SQLite persistence for governance-specific semantic state."""

from __future__ import annotations

import json
import sqlite3
from pathlib import Path
from typing import Iterable

from .governance_models import EcosystemGovernancePortfolio, PlatformGovernanceCard, GovernanceCheckResult
from .human_readiness_registry import ReadinessRegistry, ReadinessRegistryEntry
from .mcp_contract import McpContractCoverage, McpContractReport
from .models import as_plain_data, utc_now
from .round_assurance import AssuranceSuite, AssuranceCase
from .runtime_budget import RoundLineBudget, RepositoryLineBudget
from .service_order_lifecycle import RoundExecutionPackage, OrderLifecycleDecision
from .workflow_registry import WorkflowPortfolio, WorkflowEvaluation
from .governance_scenarios import ScenarioPortfolio, ScenarioEvaluation


GOVERNANCE_SCHEMA = """
CREATE TABLE IF NOT EXISTS governance_cards (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    platform_id TEXT UNIQUE NOT NULL,
    status_label TEXT NOT NULL,
    governance_score INTEGER NOT NULL,
    human_score INTEGER NOT NULL,
    maturity TEXT NOT NULL,
    blocker_count INTEGER NOT NULL,
    warning_count INTEGER NOT NULL,
    payload_json TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS governance_checks (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    check_key TEXT UNIQUE NOT NULL,
    platform_id TEXT NOT NULL,
    check_id TEXT NOT NULL,
    axis TEXT NOT NULL,
    domain TEXT NOT NULL,
    status TEXT NOT NULL,
    severity TEXT NOT NULL,
    score INTEGER NOT NULL,
    next_action TEXT NOT NULL,
    payload_json TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS readiness_registry (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    entry_id TEXT UNIQUE NOT NULL,
    platform_id TEXT NOT NULL,
    profile_id TEXT NOT NULL,
    human_score INTEGER NOT NULL,
    governance_score INTEGER NOT NULL,
    status TEXT NOT NULL,
    recommended_action TEXT NOT NULL,
    payload_json TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS workflow_evaluations (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    workflow_id TEXT UNIQUE NOT NULL,
    status TEXT NOT NULL,
    score INTEGER NOT NULL,
    passed_steps INTEGER NOT NULL,
    total_steps INTEGER NOT NULL,
    payload_json TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS scenario_evaluations (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    scenario_id TEXT UNIQUE NOT NULL,
    status TEXT NOT NULL,
    score INTEGER NOT NULL,
    assertion_count INTEGER NOT NULL,
    failed_assertion_count INTEGER NOT NULL,
    payload_json TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS assurance_cases (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    case_id TEXT UNIQUE NOT NULL,
    passed INTEGER NOT NULL,
    severity TEXT NOT NULL,
    required INTEGER NOT NULL,
    title TEXT NOT NULL,
    next_action TEXT NOT NULL,
    payload_json TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS lifecycle_decisions (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    order_id TEXT UNIQUE NOT NULL,
    final_status TEXT NOT NULL,
    platform_id TEXT NOT NULL,
    pending_count INTEGER NOT NULL,
    resulting_candidate_count INTEGER NOT NULL,
    payload_json TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS line_budgets (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    repo_name TEXT UNIQUE NOT NULL,
    exists_flag INTEGER NOT NULL,
    files_seen INTEGER NOT NULL,
    files_counted INTEGER NOT NULL,
    code_lines INTEGER NOT NULL,
    technical_lines INTEGER NOT NULL,
    warnings_json TEXT NOT NULL,
    payload_json TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS mcp_contracts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    contract_id TEXT UNIQUE NOT NULL,
    kind TEXT NOT NULL,
    platform_id TEXT NOT NULL,
    profile_id TEXT NOT NULL,
    tool_id TEXT NOT NULL,
    status TEXT NOT NULL,
    truth_state TEXT NOT NULL,
    score INTEGER NOT NULL,
    same_source INTEGER NOT NULL,
    source_payload_hash TEXT NOT NULL,
    source_records_hash TEXT NOT NULL,
    blocker_count INTEGER NOT NULL,
    next_action TEXT NOT NULL,
    payload_json TEXT NOT NULL,
    updated_at TEXT NOT NULL
);
"""


def ensure_governance_schema(conn: sqlite3.Connection) -> None:
    conn.executescript(GOVERNANCE_SCHEMA)


def payload(value: object) -> str:
    return json.dumps(as_plain_data(value), ensure_ascii=False, sort_keys=True)


def upsert_governance_card(conn: sqlite3.Connection, card: PlatformGovernanceCard, now: str) -> None:
    conn.execute(
        """
        INSERT INTO governance_cards (
            platform_id, status_label, governance_score, human_score, maturity,
            blocker_count, warning_count, payload_json, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        ON CONFLICT(platform_id) DO UPDATE SET
            status_label=excluded.status_label,
            governance_score=excluded.governance_score,
            human_score=excluded.human_score,
            maturity=excluded.maturity,
            blocker_count=excluded.blocker_count,
            warning_count=excluded.warning_count,
            payload_json=excluded.payload_json,
            updated_at=excluded.updated_at
        """,
        (
            card.platform_id,
            card.status_label,
            card.governance_score,
            card.human_score,
            card.maturity.value,
            len(card.blockers),
            len(card.warnings),
            payload(card),
            now,
        ),
    )


def upsert_governance_check(conn: sqlite3.Connection, check: GovernanceCheckResult, now: str) -> None:
    key = f"{check.platform_id}:{check.check_id}"
    conn.execute(
        """
        INSERT INTO governance_checks (
            check_key, platform_id, check_id, axis, domain, status, severity,
            score, next_action, payload_json, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ON CONFLICT(check_key) DO UPDATE SET
            axis=excluded.axis,
            domain=excluded.domain,
            status=excluded.status,
            severity=excluded.severity,
            score=excluded.score,
            next_action=excluded.next_action,
            payload_json=excluded.payload_json,
            updated_at=excluded.updated_at
        """,
        (
            key,
            check.platform_id,
            check.check_id,
            check.axis.value,
            check.domain.value,
            check.status.value,
            check.severity.value,
            check.score,
            check.next_action,
            payload(check),
            now,
        ),
    )


def upsert_registry_entry(conn: sqlite3.Connection, entry: ReadinessRegistryEntry, now: str) -> None:
    conn.execute(
        """
        INSERT INTO readiness_registry (
            entry_id, platform_id, profile_id, human_score, governance_score,
            status, recommended_action, payload_json, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        ON CONFLICT(entry_id) DO UPDATE SET
            human_score=excluded.human_score,
            governance_score=excluded.governance_score,
            status=excluded.status,
            recommended_action=excluded.recommended_action,
            payload_json=excluded.payload_json,
            updated_at=excluded.updated_at
        """,
        (
            entry.entry_id,
            entry.platform_id,
            entry.profile_id,
            entry.human_score,
            entry.governance_score,
            entry.status,
            entry.recommended_action,
            payload(entry),
            now,
        ),
    )


def upsert_workflow(conn: sqlite3.Connection, evaluation: WorkflowEvaluation, now: str) -> None:
    conn.execute(
        """
        INSERT INTO workflow_evaluations (
            workflow_id, status, score, passed_steps, total_steps, payload_json, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?)
        ON CONFLICT(workflow_id) DO UPDATE SET
            status=excluded.status,
            score=excluded.score,
            passed_steps=excluded.passed_steps,
            total_steps=excluded.total_steps,
            payload_json=excluded.payload_json,
            updated_at=excluded.updated_at
        """,
        (evaluation.workflow_id, evaluation.status, evaluation.score, evaluation.passed_steps, evaluation.total_steps, payload(evaluation), now),
    )


def upsert_scenario(conn: sqlite3.Connection, evaluation: ScenarioEvaluation, now: str) -> None:
    failed = sum(1 for item in evaluation.assertion_results if not item.passed)
    conn.execute(
        """
        INSERT INTO scenario_evaluations (
            scenario_id, status, score, assertion_count, failed_assertion_count, payload_json, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?)
        ON CONFLICT(scenario_id) DO UPDATE SET
            status=excluded.status,
            score=excluded.score,
            assertion_count=excluded.assertion_count,
            failed_assertion_count=excluded.failed_assertion_count,
            payload_json=excluded.payload_json,
            updated_at=excluded.updated_at
        """,
        (evaluation.scenario_id, evaluation.status, evaluation.score, len(evaluation.assertion_results), failed, payload(evaluation), now),
    )


def upsert_assurance_case(conn: sqlite3.Connection, case: AssuranceCase, now: str) -> None:
    conn.execute(
        """
        INSERT INTO assurance_cases (
            case_id, passed, severity, required, title, next_action, payload_json, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        ON CONFLICT(case_id) DO UPDATE SET
            passed=excluded.passed,
            severity=excluded.severity,
            required=excluded.required,
            title=excluded.title,
            next_action=excluded.next_action,
            payload_json=excluded.payload_json,
            updated_at=excluded.updated_at
        """,
        (case.case_id, 1 if case.passed else 0, case.severity, 1 if case.required else 0, case.title, case.next_action, payload(case), now),
    )


def upsert_lifecycle_decision(conn: sqlite3.Connection, decision: OrderLifecycleDecision, now: str) -> None:
    conn.execute(
        """
        INSERT INTO lifecycle_decisions (
            order_id, final_status, platform_id, pending_count, resulting_candidate_count,
            payload_json, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?)
        ON CONFLICT(order_id) DO UPDATE SET
            final_status=excluded.final_status,
            platform_id=excluded.platform_id,
            pending_count=excluded.pending_count,
            resulting_candidate_count=excluded.resulting_candidate_count,
            payload_json=excluded.payload_json,
            updated_at=excluded.updated_at
        """,
        (
            decision.order.order_id,
            decision.final_status.value,
            decision.platform_id,
            len(decision.pending_items),
            len(decision.resulting_candidates),
            payload(decision),
            now,
        ),
    )


def upsert_line_budget(conn: sqlite3.Connection, repo: RepositoryLineBudget, now: str) -> None:
    conn.execute(
        """
        INSERT INTO line_budgets (
            repo_name, exists_flag, files_seen, files_counted, code_lines,
            technical_lines, warnings_json, payload_json, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        ON CONFLICT(repo_name) DO UPDATE SET
            exists_flag=excluded.exists_flag,
            files_seen=excluded.files_seen,
            files_counted=excluded.files_counted,
            code_lines=excluded.code_lines,
            technical_lines=excluded.technical_lines,
            warnings_json=excluded.warnings_json,
            payload_json=excluded.payload_json,
            updated_at=excluded.updated_at
        """,
        (
            repo.repo_name,
            1 if repo.exists else 0,
            repo.files_seen,
            repo.files_counted,
            repo.code_lines,
            repo.technical_lines,
            json.dumps(list(repo.warnings), ensure_ascii=False),
            payload(repo),
            now,
        ),
    )


def upsert_mcp_contract(conn: sqlite3.Connection, coverage: McpContractCoverage, now: str) -> None:
    conn.execute(
        """
        INSERT INTO mcp_contracts (
            contract_id, kind, platform_id, profile_id, tool_id, status, truth_state,
            score, same_source, source_payload_hash, source_records_hash, blocker_count,
            next_action, payload_json, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ON CONFLICT(contract_id) DO UPDATE SET
            kind=excluded.kind,
            platform_id=excluded.platform_id,
            profile_id=excluded.profile_id,
            tool_id=excluded.tool_id,
            status=excluded.status,
            truth_state=excluded.truth_state,
            score=excluded.score,
            same_source=excluded.same_source,
            source_payload_hash=excluded.source_payload_hash,
            source_records_hash=excluded.source_records_hash,
            blocker_count=excluded.blocker_count,
            next_action=excluded.next_action,
            payload_json=excluded.payload_json,
            updated_at=excluded.updated_at
        """,
        (
            coverage.contract_id,
            coverage.kind.value,
            coverage.platform_id,
            coverage.profile_id,
            coverage.tool_id,
            coverage.status.value,
            coverage.truth_state.value,
            coverage.score,
            1 if coverage.same_source else 0,
            coverage.source_payload_hash,
            coverage.source_records_hash,
            len(coverage.blockers),
            coverage.next_action,
            payload(coverage),
            now,
        ),
    )


def write_governance_semantic_state(
    sqlite_path: Path,
    portfolio: EcosystemGovernancePortfolio,
    registry: ReadinessRegistry,
    workflows: WorkflowPortfolio,
    scenarios: ScenarioPortfolio,
    assurance: AssuranceSuite | None = None,
    lifecycle: RoundExecutionPackage | None = None,
    budget: RoundLineBudget | None = None,
    mcp_contract_report: McpContractReport | None = None,
) -> None:
    sqlite_path.parent.mkdir(parents=True, exist_ok=True)
    now = utc_now()
    with sqlite3.connect(sqlite_path) as conn:
        ensure_governance_schema(conn)
        for card in portfolio.cards:
            upsert_governance_card(conn, card, now)
            for check in card.checks:
                upsert_governance_check(conn, check, now)
        for entry in registry.entries:
            upsert_registry_entry(conn, entry, now)
        for evaluation in workflows.evaluations:
            upsert_workflow(conn, evaluation, now)
        for evaluation in scenarios.evaluations:
            upsert_scenario(conn, evaluation, now)
        if assurance is not None:
            for case in assurance.cases:
                upsert_assurance_case(conn, case, now)
        if lifecycle is not None:
            for decision in lifecycle.decisions:
                upsert_lifecycle_decision(conn, decision, now)
        if budget is not None:
            for repo in budget.repositories:
                upsert_line_budget(conn, repo, now)
        if mcp_contract_report is not None:
            for coverage in mcp_contract_report.coverage:
                upsert_mcp_contract(conn, coverage, now)
        conn.commit()


def governance_table_counts(sqlite_path: Path) -> dict[str, int]:
    tables = (
        "governance_cards",
        "governance_checks",
        "readiness_registry",
        "workflow_evaluations",
        "scenario_evaluations",
        "assurance_cases",
        "lifecycle_decisions",
        "line_budgets",
        "mcp_contracts",
    )
    if not sqlite_path.exists():
        return {table: 0 for table in tables}
    with sqlite3.connect(sqlite_path) as conn:
        ensure_governance_schema(conn)
        return {table: int(conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0]) for table in tables}
