import json
from datetime import datetime
from typing import Any, Dict, List, Optional

from sqlalchemy.orm import Session, selectinload

from app.models.domain import Domain
from app.models.report import DMARCReport, ReportRecord
from app.services.report_store import ReportStore


def _parse_timestamp(value: Any) -> int:
    """Return a Unix timestamp from an int-like or ISO date value."""
    if value in (None, ""):
        return 0
    if isinstance(value, (int, float)):
        return int(value)
    try:
        return int(value)
    except (TypeError, ValueError):
        pass
    try:
        return int(datetime.fromisoformat(str(value)).timestamp())
    except (TypeError, ValueError):
        return 0


def _iso_from_timestamp(value: int) -> str:
    if not value:
        return ""
    return datetime.fromtimestamp(value).isoformat()


def _loads_json_list(value: Optional[str]) -> Optional[List[Dict[str, Any]]]:
    if not value:
        return None
    try:
        decoded = json.loads(value)
    except (json.JSONDecodeError, TypeError):
        return None
    return decoded if isinstance(decoded, list) else None


def _policy_parts(report: Dict[str, Any]) -> Dict[str, Any]:
    policy = report.get("policy") or {}
    if isinstance(policy, str):
        return {"p": policy, "sp": "", "pct": "100"}
    if not isinstance(policy, dict):
        return {"p": "none", "sp": "", "pct": "100"}
    return {
        "p": policy.get("p", "none"),
        "sp": policy.get("sp", ""),
        "pct": str(policy.get("pct", "100")),
        "adkim": policy.get("adkim") or report.get("adkim"),
        "aspf": policy.get("aspf") or report.get("aspf"),
    }


def report_exists(db: Session, domain_name: str, report_id: str) -> bool:
    """Return True when the domain/report ID pair is already persisted."""
    if not report_id:
        return False
    return (
        db.query(DMARCReport.id)
        .join(Domain, DMARCReport.domain_id == Domain.id)
        .filter(Domain.name == domain_name, DMARCReport.report_id == report_id)
        .first()
        is not None
    )


def save_parsed_report(db: Session, report: Dict[str, Any]) -> tuple[DMARCReport, bool]:
    """Persist a parsed DMARC report and its records.

    Returns ``(row, created)``. The caller owns the transaction and should
    commit after all related work has completed.
    """
    domain_name = report.get("domain") or "unknown"
    report_id = report.get("report_id") or ""
    policy = _policy_parts(report)

    domain = db.query(Domain).filter(Domain.name == domain_name).first()
    if domain is None:
        domain = Domain(name=domain_name, dmarc_policy=policy["p"])
        db.add(domain)
        db.flush()
    elif policy.get("p"):
        domain.dmarc_policy = policy["p"]

    existing = (
        db.query(DMARCReport)
        .filter(DMARCReport.domain_id == domain.id, DMARCReport.report_id == report_id)
        .first()
    )
    if existing is not None:
        return existing, False

    begin_ts = _parse_timestamp(report.get("begin_timestamp") or report.get("begin_date"))
    end_ts = _parse_timestamp(report.get("end_timestamp") or report.get("end_date"))
    pct = _parse_timestamp(policy.get("pct")) or 100

    db_report = DMARCReport(
        domain_id=domain.id,
        report_id=report_id,
        org_name=report.get("org_name") or "",
        begin_date=begin_ts,
        end_date=end_ts,
        source_email=report.get("email") or report.get("source_email"),
        policy=policy["p"],
        subdomain_policy=policy.get("sp") or None,
        adkim=policy.get("adkim") or None,
        aspf=policy.get("aspf") or None,
        percentage=pct,
    )
    db.add(db_report)
    db.flush()

    for record in report.get("records", []):
        db.add(
            ReportRecord(
                report_id=db_report.id,
                source_ip=record.get("source_ip") or "unknown",
                count=int(record.get("count") or 0),
                disposition=record.get("disposition") or "none",
                dkim=record.get("dkim_result") or record.get("dkim") or "unknown",
                spf=record.get("spf_result") or record.get("spf") or "unknown",
                header_from=record.get("header_from"),
                envelope_from=record.get("envelope_from"),
                dkim_auth_details=(
                    json.dumps(record.get("dkim")) if isinstance(record.get("dkim"), list) else None
                ),
                spf_auth_details=(
                    json.dumps(record.get("spf")) if isinstance(record.get("spf"), list) else None
                ),
            )
        )

    return db_report, True


def persisted_report_to_dict(report: DMARCReport) -> Dict[str, Any]:
    """Convert persisted report rows into the parsed-report shape used by the UI."""
    records: List[Dict[str, Any]] = []
    total_count = 0
    passed_count = 0

    for record in report.records:
        count = int(record.count or 0)
        dkim_result = record.dkim or "unknown"
        spf_result = record.spf or "unknown"
        total_count += count
        if dkim_result == "pass" or spf_result == "pass":
            passed_count += count

        records.append(
            {
                "source_ip": record.source_ip,
                "count": count,
                "disposition": record.disposition or "none",
                "dkim_result": dkim_result,
                "spf_result": spf_result,
                "header_from": record.header_from or "",
                "dkim": _loads_json_list(record.dkim_auth_details) or [],
                "spf": _loads_json_list(record.spf_auth_details) or [],
            }
        )

    failed_count = total_count - passed_count
    pass_rate = round(passed_count / total_count * 100, 1) if total_count > 0 else 0.0
    return {
        "domain": report.domain.name if report.domain else "unknown",
        "report_id": report.report_id,
        "org_name": report.org_name,
        "email": report.source_email or "",
        "begin_date": _iso_from_timestamp(report.begin_date),
        "end_date": _iso_from_timestamp(report.end_date),
        "begin_timestamp": report.begin_date,
        "end_timestamp": report.end_date,
        "policy": {
            "p": report.policy or "none",
            "sp": report.subdomain_policy or "",
            "pct": str(report.percentage or 100),
        },
        "records": records,
        "summary": {
            "total_count": total_count,
            "passed_count": passed_count,
            "failed_count": failed_count,
            "pass_rate": pass_rate,
        },
    }


def hydrate_report_store_from_db(db: Session, store: ReportStore | None = None) -> int:
    """Load persisted reports into ReportStore when the database has report rows."""
    report_count = db.query(DMARCReport.id).count()
    if report_count == 0:
        return 0

    store = store or ReportStore.get_instance()
    store.clear()
    reports = (
        db.query(DMARCReport)
        .options(
            selectinload(DMARCReport.domain),
            selectinload(DMARCReport.records),
        )
        .order_by(DMARCReport.end_date.desc())
        .all()
    )
    for report in reports:
        store.add_report(persisted_report_to_dict(report))
    return len(reports)


def delete_persisted_report(db: Session, domain_name: str, report_id: str) -> bool:
    """Delete a persisted report by domain/report ID."""
    report = (
        db.query(DMARCReport)
        .join(Domain, DMARCReport.domain_id == Domain.id)
        .filter(Domain.name == domain_name, DMARCReport.report_id == report_id)
        .first()
    )
    if report is None:
        return False
    db.delete(report)
    return True


def delete_persisted_domain(db: Session, domain_name: str) -> bool:
    """Delete a domain row and cascaded report data."""
    domain = db.query(Domain).filter(Domain.name == domain_name).first()
    if domain is None:
        return False
    db.delete(domain)
    return True
