"""
Salesforce metadata smoke assertions.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Dict, List

from g_gremlin import sfdc as sfdc_helpers
from g_gremlin import sfdc_schema as sfdc_schema_helpers


@dataclass
class SmokeAssertion:
    type: str
    params: Dict[str, Any]


@dataclass
class SmokeResult:
    assertion: SmokeAssertion
    passed: bool
    detail: str = ""

    @property
    def skipped(self) -> bool:
        return self.detail.startswith("skipped:")

    def to_dict(self) -> Dict[str, Any]:
        return {
            "type": self.assertion.type,
            "params": self.assertion.params,
            "passed": self.passed,
            "detail": self.detail,
        }


@dataclass
class SmokeReport:
    results: List[SmokeResult]

    @property
    def all_passed(self) -> bool:
        return all(r.passed or r.skipped for r in self.results)

    @property
    def summary(self) -> Dict[str, int]:
        total = len(self.results)
        passed = len([r for r in self.results if r.passed])
        failed = len([r for r in self.results if (not r.passed) and (not r.skipped)])
        skipped = len([r for r in self.results if r.skipped])
        return {"total": total, "passed": passed, "failed": failed, "skipped": skipped}

    def to_dict(self) -> Dict[str, Any]:
        return {"summary": self.summary, "results": [r.to_dict() for r in self.results]}


def load_smoke_assertions(pack_yaml_dict: Dict[str, Any]) -> List[SmokeAssertion]:
    items = pack_yaml_dict.get("smoke") or []
    assertions: List[SmokeAssertion] = []
    if isinstance(items, list):
        for item in items:
            if not isinstance(item, dict):
                continue
            atype = item.get("type") or item.get("assertion")
            params = item.get("params") or {k: v for k, v in item.items() if k not in {"type", "assertion"}}
            if atype:
                assertions.append(SmokeAssertion(type=str(atype), params=params))
    return assertions


def run_smoke(assertions: List[SmokeAssertion], *, org_alias: str | None = None) -> SmokeReport:
    results: List[SmokeResult] = []
    rule_cache: List[Dict[str, Any]] | None = None

    for assertion in assertions:
        atype = assertion.type
        params = assertion.params or {}

        if atype == "field_exists":
            results.append(_assert_field_exists(params, org_alias=org_alias))
            continue

        if atype == "permset_exists":
            results.append(_assert_permset_exists(params, org_alias=org_alias))
            continue

        if atype == "queue_exists":
            results.append(_assert_queue_exists(params, org_alias=org_alias))
            continue

        if atype == "validation_rule_exists":
            rule_cache = rule_cache or _fetch_validation_rules(org_alias=org_alias)
            results.append(_assert_validation_rule_exists(params, rule_cache))
            continue

        if atype == "record_type_exists":
            results.append(_assert_record_type_exists(params, org_alias=org_alias))
            continue

        raise ValueError(f"Unknown smoke assertion type: {atype}")

    return SmokeReport(results=results)


# ---- assertion helpers ----


def _assert_field_exists(params: Dict[str, Any], *, org_alias: str | None) -> SmokeResult:
    target = str(params.get("target") or params.get("field") or "")
    if not target or "." not in target:
        return SmokeResult(
            assertion=SmokeAssertion(type="field_exists", params=params),
            passed=False,
            detail="missing target (expected Object.Field__c)",
        )
    obj, field = target.split(".", 1)
    try:
        identity = sfdc_helpers.get_org_identity(org_alias=org_alias)
        schema_info = sfdc_schema_helpers.get_schema(obj, identity=identity, org_alias=org_alias)
    except Exception as exc:
        return SmokeResult(
            assertion=SmokeAssertion(type="field_exists", params=params),
            passed=False,
            detail=str(exc),
        )
    passed = field in schema_info.fields
    detail = "" if passed else f"field not found: {target}"
    return SmokeResult(assertion=SmokeAssertion(type="field_exists", params=params), passed=passed, detail=detail)


def _assert_permset_exists(params: Dict[str, Any], *, org_alias: str | None) -> SmokeResult:
    name = str(params.get("target") or params.get("name") or "")
    if not name:
        return SmokeResult(
            assertion=SmokeAssertion(type="permset_exists", params=params),
            passed=False,
            detail="missing permset name",
        )
    soql = (
        "SELECT Id FROM PermissionSet "
        f"WHERE Name = '{sfdc_helpers.escape_soql_value(name)}' LIMIT 1"
    )
    try:
        rows = sfdc_helpers.run_soql(soql, org_alias=org_alias)
    except Exception as exc:
        return SmokeResult(
            assertion=SmokeAssertion(type="permset_exists", params=params),
            passed=False,
            detail=str(exc),
        )
    passed = bool(rows)
    detail = "" if passed else f"permission set not found: {name}"
    return SmokeResult(assertion=SmokeAssertion(type="permset_exists", params=params), passed=passed, detail=detail)


def _assert_queue_exists(params: Dict[str, Any], *, org_alias: str | None) -> SmokeResult:
    name = str(params.get("target") or params.get("name") or "")
    if not name:
        return SmokeResult(
            assertion=SmokeAssertion(type="queue_exists", params=params),
            passed=False,
            detail="missing queue name",
        )
    escaped = sfdc_helpers.escape_soql_value(name)
    soql = (
        "SELECT Id FROM Group WHERE Type = 'Queue' AND "
        f"(DeveloperName = '{escaped}' OR Name = '{escaped}') LIMIT 1"
    )
    try:
        rows = sfdc_helpers.run_soql(soql, org_alias=org_alias)
    except Exception as exc:
        return SmokeResult(
            assertion=SmokeAssertion(type="queue_exists", params=params),
            passed=False,
            detail=str(exc),
        )
    passed = bool(rows)
    detail = "" if passed else f"queue not found: {name}"
    return SmokeResult(assertion=SmokeAssertion(type="queue_exists", params=params), passed=passed, detail=detail)


def _fetch_validation_rules(org_alias: str | None) -> List[Dict[str, Any]]:
    try:
        return list(sfdc_helpers.org_list_metadata("ValidationRule", org_alias=org_alias) or [])
    except Exception:
        return []


def _assert_validation_rule_exists(params: Dict[str, Any], rules: List[Dict[str, Any]]) -> SmokeResult:
    target = str(params.get("target") or params.get("name") or "")
    if not target or "." not in target:
        return SmokeResult(
            assertion=SmokeAssertion(type="validation_rule_exists", params=params),
            passed=False,
            detail="missing target (expected Object.Rule)",
        )
    target_lower = target.lower()
    for item in rules:
        full_name = str(item.get("fullName") or item.get("full_name") or item.get("name") or "")
        if full_name.lower() == target_lower:
            return SmokeResult(
                assertion=SmokeAssertion(type="validation_rule_exists", params=params),
                passed=True,
                detail="",
            )
    return SmokeResult(
        assertion=SmokeAssertion(type="validation_rule_exists", params=params),
        passed=False,
        detail=f"validation rule not found: {target}",
    )


def _assert_record_type_exists(params: Dict[str, Any], *, org_alias: str | None) -> SmokeResult:
    target = str(params.get("target") or params.get("name") or "")
    if not target or "." not in target:
        return SmokeResult(
            assertion=SmokeAssertion(type="record_type_exists", params=params),
            passed=False,
            detail="missing target (expected Object.RecordType)",
        )
    obj, record_type = target.split(".", 1)
    escaped_obj = sfdc_helpers.escape_soql_value(obj)
    escaped_rt = sfdc_helpers.escape_soql_value(record_type)
    soql = (
        "SELECT Id FROM RecordType "
        f"WHERE SObjectType = '{escaped_obj}' "
        f"AND (DeveloperName = '{escaped_rt}' OR Name = '{escaped_rt}') LIMIT 1"
    )
    try:
        rows = sfdc_helpers.run_soql(soql, org_alias=org_alias)
    except Exception as exc:
        return SmokeResult(
            assertion=SmokeAssertion(type="record_type_exists", params=params),
            passed=False,
            detail=str(exc),
        )
    passed = bool(rows)
    detail = "" if passed else f"record type not found: {target}"
    return SmokeResult(
        assertion=SmokeAssertion(type="record_type_exists", params=params),
        passed=passed,
        detail=detail,
    )
