"""NimbusClient — main entry point for the Nimbus SDK."""

from __future__ import annotations

import json
import logging
from pathlib import Path
from typing import Any, Callable, Optional

from nimbus_core.bedrock import (
    DEFAULT_MODEL_ID,
    DEFAULT_REGION,
    get_remediation,
    normalize_finding,
)
from nimbus_core.infra import check_aws_credentials, destroy_pipeline, provision_pipeline
from nimbus_core.models import FindingInput, InitResult, RemediationResult

logger = logging.getLogger(__name__)

_CONFIG_PATH = Path.home() / ".nimbus-cli.json"


def _load_config() -> dict:
    if _CONFIG_PATH.exists():
        return json.loads(_CONFIG_PATH.read_text())
    return {}


def _save_config(cfg: dict) -> None:
    _CONFIG_PATH.write_text(json.dumps(cfg, indent=2) + "\n")


class NimbusClient:
    """Nimbus AWS Security SDK client.

    Usage::

        # With existing KB
        client = NimbusClient(region="us-east-1", kb_id="YOUR_KB_ID")
        result = client.suggest_fix("S3 bucket is publicly accessible")

        # Provision new KB first
        client = NimbusClient(region="us-east-1")
        client.init()
        result = client.suggest_fix("EC2 instance has no IMDSv2")
    """

    def __init__(
        self,
        *,
        region: Optional[str] = None,
        kb_id: Optional[str] = None,
        model_id: Optional[str] = None,
        account_id: Optional[str] = None,
    ) -> None:
        cfg = _load_config()
        self.region = region or cfg.get("bedrock_region") or cfg.get("region") or DEFAULT_REGION
        self.kb_id = kb_id or cfg.get("kb_id")
        self.account_id = account_id or cfg.get("account_id")
        self.model_id = model_id or cfg.get("bedrock_model_id") or DEFAULT_MODEL_ID

    @property
    def model_arn(self) -> str:
        acct = self.account_id
        if not acct:
            # Resolve account ID lazily
            acct, _ = check_aws_credentials(self.region)
            self.account_id = acct
        return (
            f"arn:aws:bedrock:{self.region}:{acct}:inference-profile/{self.model_id}"
        )

    def init(
        self,
        *,
        project: str = "nimbus",
        docs_dir: Optional[Path] = None,
        on_step: Optional[Callable[[int, str], None]] = None,
    ) -> InitResult:
        """Provision the full Bedrock RAG pipeline.

        Creates S3 docs bucket, S3 Vectors, IAM role, Bedrock KB,
        uploads documents, and starts ingestion.

        Args:
            project: Resource name prefix.
            docs_dir: Custom documents directory (auto-detected if None).
            on_step: Optional callback(step_number, description) for progress.

        Returns:
            InitResult with provisioned resource identifiers.
        """
        result = provision_pipeline(
            region=self.region,
            project=project,
            docs_dir=docs_dir,
            on_step=on_step,
        )
        self.kb_id = result.kb_id
        self.account_id = result.account_id
        self.region = result.region

        # Config is saved by provision_pipeline itself (incrementally)
        return result

    def suggest_fix(self, text: str) -> RemediationResult:
        """Get an AI-powered remediation for a security problem.

        Args:
            text: Description of the security issue.

        Returns:
            RemediationResult with the AI remediation and normalized finding.

        Raises:
            RuntimeError: If no KB is configured or Bedrock call fails.
        """
        if not self.kb_id:
            raise RuntimeError(
                "No Knowledge Base configured. "
                "Run client.init() or pass kb_id to NimbusClient()."
            )

        finding = FindingInput(title=text, description=text)
        normalized = normalize_finding(finding)
        remediation_text = get_remediation(
            normalized,
            kb_id=self.kb_id,
            model_arn=self.model_arn,
            bedrock_region=self.region,
        )
        return RemediationResult(
            remediation=remediation_text,
            normalized_finding=normalized,
        )

    def suggest_fix_for_finding(self, finding: FindingInput) -> RemediationResult:
        """Get AI remediation for a structured finding.

        Args:
            finding: A FindingInput with full details.

        Returns:
            RemediationResult with the AI remediation and normalized finding.
        """
        if not self.kb_id:
            raise RuntimeError(
                "No Knowledge Base configured. "
                "Run client.init() or pass kb_id to NimbusClient()."
            )

        normalized = normalize_finding(finding)
        remediation_text = get_remediation(
            normalized,
            kb_id=self.kb_id,
            model_arn=self.model_arn,
            bedrock_region=self.region,
        )
        return RemediationResult(
            remediation=remediation_text,
            normalized_finding=normalized,
        )

    def destroy(
        self,
        on_step: Optional[Callable[[int, str], None]] = None,
    ) -> list[str]:
        """Tear down all AWS resources created by init().

        Deletes the Bedrock KB, S3 buckets, vector store, IAM role,
        and removes the local config file.

        Args:
            on_step: Optional callback(step_number, description) for progress.

        Returns:
            List of error messages (empty if all resources deleted successfully).
        """
        cfg = _load_config()
        if not cfg:
            raise RuntimeError("No config found. Nothing to destroy.")
        errors = destroy_pipeline(config=cfg, on_step=on_step)
        if not errors:
            self.kb_id = None
        return errors
