from __future__ import annotations

import json
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, List, Optional, Set

try:
    import yaml  # type: ignore
except Exception:  # pragma: no cover - optional dependency
    yaml = None


@dataclass
class FerruccioConfig:
    strict: Optional[bool] = None
    include_suspicious: Optional[bool] = None
    allow_sinks: Set[str] = field(default_factory=set)
    deny_sinks: Set[str] = field(default_factory=set)
    ignore_paths: List[str] = field(default_factory=list)
    vuln_db: Optional[Path] = None
    severity_threshold: Optional[str] = None

    @classmethod
    def merge(cls, base: "FerruccioConfig", other: "FerruccioConfig") -> "FerruccioConfig":
        merged = FerruccioConfig()
        merged.strict = other.strict if other.strict is not None else base.strict
        merged.include_suspicious = other.include_suspicious if other.include_suspicious is not None else base.include_suspicious
        merged.allow_sinks = base.allow_sinks.union(other.allow_sinks)
        merged.deny_sinks = base.deny_sinks.union(other.deny_sinks)
        merged.ignore_paths = base.ignore_paths + other.ignore_paths
        merged.vuln_db = other.vuln_db or base.vuln_db
        merged.severity_threshold = other.severity_threshold or base.severity_threshold
        return merged


def _load_yaml_or_json(path: Path) -> Dict:
    text = path.read_text(encoding="utf-8")
    if yaml:
        try:
            data = yaml.safe_load(text)
            if isinstance(data, dict):
                return data
        except Exception:
            pass
    try:
        data = json.loads(text)
        if isinstance(data, dict):
            return data
    except Exception:
        pass
    return {}


def load_config(config_path: Optional[Path]) -> FerruccioConfig:
    if config_path is None:
        # Try local .ferruccio.yml/.yaml/.json
        for candidate in [Path(".ferruccio.yml"), Path(".ferruccio.yaml"), Path(".ferruccio.json")]:
            if candidate.exists():
                config_path = candidate
                break
    if config_path is None or not config_path.exists():
        return FerruccioConfig()

    data = _load_yaml_or_json(config_path)
    cfg = FerruccioConfig()
    cfg.strict = data.get("strict")
    cfg.include_suspicious = data.get("include_suspicious")
    cfg.allow_sinks = set(data.get("allow_sinks", []))
    cfg.deny_sinks = set(data.get("deny_sinks", []))
    cfg.ignore_paths = data.get("ignore_paths", []) or []
    cfg.severity_threshold = data.get("severity_threshold")
    vuln_db = data.get("vuln_db")
    if vuln_db:
        cfg.vuln_db = Path(vuln_db)
    return cfg
