"""Configuration management for Nogic CLI."""

import os
import json
import uuid
from pathlib import Path
from dataclasses import dataclass, field
from typing import Optional

from dotenv import load_dotenv

load_dotenv()

CONFIG_FILE = "config.json"
PROJECTS_FILE = "projects.json"
GLOBAL_CONFIG_DIR = Path.home() / ".nogic"

_PRODUCTION_URL = "https://api.nogic.dev"


def get_or_create_machine_id() -> str:
    """Get or create a unique machine identifier stored at ~/.nogic/machine_id."""
    machine_id_path = GLOBAL_CONFIG_DIR / "machine_id"
    if machine_id_path.exists():
        try:
            mid = machine_id_path.read_text().strip()
            if mid:
                return mid
        except OSError:
            pass

    mid = str(uuid.uuid4())
    GLOBAL_CONFIG_DIR.mkdir(mode=0o700, exist_ok=True)
    machine_id_path.write_text(mid)
    os.chmod(machine_id_path, 0o600)
    return mid


def is_dev_mode() -> bool:
    """True when NOGIC_API_URL env var overrides the production URL."""
    return bool(os.getenv("NOGIC_API_URL"))


def get_api_url() -> str:
    """Return NOGIC_API_URL if set, otherwise the production URL."""
    return os.getenv("NOGIC_API_URL", _PRODUCTION_URL).strip().rstrip("/")


def dev_mode_banner() -> str | None:
    """Return a dev mode banner string, or None if in production mode."""
    if is_dev_mode():
        return f"[DEV] Using {get_api_url()}"
    return None


@dataclass
class Config:
    api_key: Optional[str]
    project_id: Optional[str]
    project_name: Optional[str] = None
    directory_hash: Optional[str] = None

    @property
    def api_url(self) -> str:
        return get_api_url()

    @classmethod
    def load(cls, directory: Path = Path.cwd()) -> "Config":
        """
        Load config from multiple sources (priority order):
        1. Environment variables
        2. Global config: ~/.nogic/config.json (api_key)
        3. Global projects mapping: ~/.nogic/projects.json (project_id, project_name)
        """
        api_key = os.getenv("NOGIC_API_KEY")
        project_id = None
        project_name = None
        directory_hash = None

        global_config_path = GLOBAL_CONFIG_DIR / CONFIG_FILE
        if global_config_path.exists():
            try:
                with open(global_config_path, encoding="utf-8") as f:
                    data = json.load(f)
                    if not api_key:
                        api_key = data.get("api_key") or data.get("access_token")
            except (json.JSONDecodeError, OSError):
                pass  # Corrupted config - continue with defaults

        # Resolve project_id from global projects.json mapping
        mapping_path = GLOBAL_CONFIG_DIR / PROJECTS_FILE
        if mapping_path.exists():
            try:
                with open(mapping_path, encoding="utf-8") as f:
                    mapping = json.load(f)
                abs_dir = str(directory.resolve())
                entry = mapping.get(abs_dir)
                if isinstance(entry, dict) and entry.get("project_id"):
                    try:
                        uuid.UUID(entry["project_id"])
                        project_id = entry["project_id"]
                        project_name = entry.get("project_name") or project_name
                        directory_hash = entry.get("directory_hash") or directory_hash
                    except (ValueError, AttributeError):
                        pass  # Skip invalid project_id
            except (json.JSONDecodeError, OSError):
                pass

        if api_key:
            api_key = api_key.strip()

        return cls(
            api_key=api_key,
            project_id=project_id,
            project_name=project_name,
            directory_hash=directory_hash,
        )

    def save_global(self):
        """Save API key to ~/.nogic/config.json."""
        GLOBAL_CONFIG_DIR.mkdir(mode=0o700, exist_ok=True)

        existing = {}
        config_path = GLOBAL_CONFIG_DIR / CONFIG_FILE
        if config_path.exists():
            try:
                with open(config_path, encoding="utf-8") as f:
                    existing = json.load(f)
            except (json.JSONDecodeError, OSError):
                pass  # Corrupted config - will be overwritten

        # Clean up legacy fields
        existing.pop("access_token", None)
        existing.pop("refresh_token", None)
        existing.pop("api_url", None)
        existing.pop("keys", None)

        if self.api_key:
            existing["api_key"] = self.api_key
        else:
            existing.pop("api_key", None)

        with open(config_path, "w", encoding="utf-8") as f:
            json.dump(existing, f, indent=2)

        os.chmod(config_path, 0o600)

    def _save_project_mapping(self, directory: Path):
        """Update ~/.nogic/projects.json with directory → project_id mapping."""
        if not self.project_id:
            return

        GLOBAL_CONFIG_DIR.mkdir(mode=0o700, exist_ok=True)
        mapping_path = GLOBAL_CONFIG_DIR / PROJECTS_FILE

        existing = {}
        if mapping_path.exists():
            try:
                with open(mapping_path, encoding="utf-8") as f:
                    existing = json.load(f)
            except (json.JSONDecodeError, OSError):
                pass

        abs_dir = str(directory.resolve())
        existing[abs_dir] = {
            "project_id": self.project_id,
            "project_name": self.project_name or "",
            "directory_hash": self.directory_hash or "",
        }

        with open(mapping_path, "w", encoding="utf-8") as f:
            json.dump(existing, f, indent=2)

        os.chmod(mapping_path, 0o600)

    def save(self, directory: Path = Path.cwd()):
        """Save global config and project mapping."""
        self.save_global()
        self._save_project_mapping(directory)


def get_cli_db_path(project_id: str) -> Path:
    """Get the CLI's sync database path: ~/.nogic/sync/<project_id>/data.db"""
    try:
        uuid.UUID(project_id)
    except (ValueError, AttributeError):
        raise ValueError(f"Invalid project_id: {project_id}")
    sync_dir = GLOBAL_CONFIG_DIR / "sync" / project_id
    sync_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
    return sync_dir / "data.db"


def get_language(path: Path) -> Optional[str]:
    """Get language from file extension."""
    ext_map = {
        ".py": "python",
        ".js": "javascript",
        ".jsx": "javascript",
        ".ts": "typescript",
        ".tsx": "typescript",
    }
    return ext_map.get(path.suffix.lower())
