"""Unified PAT resolution and storage for all supported forge types."""

from __future__ import annotations

import configparser
import json
import logging
import os
from dataclasses import dataclass
from pathlib import Path

log = logging.getLogger(__name__)

AUTH_FILE = Path.home() / ".config" / "project-hub" / "auth.json"
PYTHON_GITLAB_CFG = Path.home() / ".python-gitlab.cfg"


@dataclass
class AuthInfo:
    """Authentication status for a single forge instance."""
    instance: str
    has_token: bool
    authenticated: bool


def _resolve_from_auth_file(instance: str) -> str | None:
    """Try AUTH_FILE (~/.config/project-hub/auth.json)."""
    if not AUTH_FILE.exists():
        return None
    file_stat = AUTH_FILE.stat()
    if file_stat.st_mode & 0o077:
        log.warning("auth.json has too-open permissions (%s), consider chmod 600", oct(file_stat.st_mode))
    try:
        data = json.loads(AUTH_FILE.read_text())
        return data.get(instance)
    except (json.JSONDecodeError, OSError):
        return None


def _resolve_from_gitlab_env(instance: str) -> str | None:
    """Try GITLAB_TOKEN env var (gitlab.com only)."""
    if instance != "gitlab.com":
        return None
    return os.environ.get("GITLAB_TOKEN") or None


def _resolve_from_github_env(instance: str) -> str | None:  # pylint: disable=unused-argument
    """Try GITHUB_TOKEN env var (any GitHub instance)."""
    return os.environ.get("GITHUB_TOKEN") or None


def _resolve_from_python_gitlab_cfg(instance: str) -> str | None:
    """Try ~/.python-gitlab.cfg (INI, configparser)."""
    if not PYTHON_GITLAB_CFG.exists():
        return None
    cfg = configparser.ConfigParser()
    cfg.read(PYTHON_GITLAB_CFG)
    target_url = f"https://{instance}"

    for section in cfg.sections():
        if section == "global":
            continue
        if cfg.get(section, "url", fallback=None) == target_url:
            token = cfg.get(section, "private_token", fallback=None)
            if token:
                return token

    default_section = cfg.get("global", "default", fallback=None)
    if default_section and cfg.has_section(default_section):
        if cfg.get(default_section, "url", fallback=None) == target_url:
            return cfg.get(default_section, "private_token", fallback=None)

    return None


def resolve_token(instance: str, forge_type: str | None = None) -> str | None:
    """Return a token for *instance*, or None if nothing is found.

    Resolution order:
    1. AUTH_FILE (~/.config/project-hub/auth.json) — always checked first
    2. forge_type-specific env fallback:
       - "gitlab": GITLAB_TOKEN (gitlab.com only), then ~/.python-gitlab.cfg
       - "github": GITHUB_TOKEN
       - None: no env fallbacks
    """
    token = _resolve_from_auth_file(instance)
    if token:
        return token

    if forge_type == "gitlab":
        token = _resolve_from_gitlab_env(instance)
        if token:
            return token
        return _resolve_from_python_gitlab_cfg(instance)

    if forge_type == "github":
        return _resolve_from_github_env(instance)

    return None


def store_token(instance: str, token: str) -> None:
    """Persist *token* for *instance* in AUTH_FILE, creating it if needed."""
    AUTH_FILE.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
    os.chmod(str(AUTH_FILE.parent), 0o700)

    data: dict[str, str] = {}
    if AUTH_FILE.exists():
        try:
            data = json.loads(AUTH_FILE.read_text())
        except (json.JSONDecodeError, OSError):
            pass

    data[instance] = token
    fd = os.open(str(AUTH_FILE), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
    with os.fdopen(fd, 'w') as f:
        json.dump(data, f, indent=2)


def auth_status() -> list[AuthInfo]:
    """Return an AuthInfo entry for every instance recorded in AUTH_FILE."""
    if not AUTH_FILE.exists():
        return []

    try:
        data = json.loads(AUTH_FILE.read_text())
    except (json.JSONDecodeError, OSError):
        return []

    return [
        AuthInfo(instance=inst, has_token=bool(tok), authenticated=bool(tok))
        for inst, tok in data.items()
    ]
