# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Email Account Registry — multi-account storage and lookup.

Stores accounts in ~/.familiar/data/email_accounts.json (chmod 600).
Provides transparent migration from legacy EMAIL_* env vars.
"""

import json
import logging
import os
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)

# Provider detection: IMAP server -> provider key
_SERVER_TO_PROVIDER = {
    "imap.gmail.com": "gmail",
    "outlook.office365.com": "outlook",
    "imap.mail.yahoo.com": "yahoo",
    "127.0.0.1": "proton",
}


def _data_dir() -> Path:
    try:
        from familiar.core.paths import DATA_DIR

        return DATA_DIR
    except ImportError:
        return Path.home() / ".familiar" / "data"


def _accounts_file() -> Path:
    return _data_dir() / "email_accounts.json"


def _load_accounts_file() -> dict:
    """Read JSON registry. Returns {"accounts": [], "primary": None} if missing."""
    path = _accounts_file()
    if not path.exists():
        return {"accounts": [], "primary": None}
    try:
        data = json.loads(path.read_text())
        if "accounts" not in data:
            data["accounts"] = []
        if "primary" not in data:
            data["primary"] = None
        return data
    except (json.JSONDecodeError, OSError) as e:
        logger.error(f"Failed to read email accounts: {e}")
        return {"accounts": [], "primary": None}


def _save_accounts_file(data: dict):
    """Write JSON registry, chmod 600."""
    path = _accounts_file()
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, indent=2) + "\n")
    path.chmod(0o600)


def _detect_provider(imap_server: str) -> str:
    """Detect provider key from IMAP server address."""
    return _SERVER_TO_PROVIDER.get(imap_server, "custom")


def _migrate_env_vars() -> Optional[dict]:
    """If EMAIL_ADDRESS is set but no JSON exists, build an account from env vars."""
    addr = os.environ.get("EMAIL_ADDRESS", "")
    password = os.environ.get("EMAIL_PASSWORD", "")
    if not addr or not password:
        return None
    imap_server = os.environ.get("EMAIL_IMAP_SERVER", "imap.gmail.com")
    provider = _detect_provider(imap_server)
    return {
        "id": provider,
        "address": addr,
        "password": password,
        "imap_server": imap_server,
        "smtp_server": os.environ.get("EMAIL_SMTP_SERVER", "smtp.gmail.com"),
        "imap_port": int(os.environ.get("EMAIL_IMAP_PORT", "993")),
        "smtp_port": int(os.environ.get("EMAIL_SMTP_PORT", "587")),
        "provider": provider,
    }


def get_all_accounts() -> list[dict]:
    """Load all accounts, auto-migrating from env vars if registry is empty."""
    data = _load_accounts_file()
    if data["accounts"]:
        return data["accounts"]
    # Auto-migrate from env vars
    migrated = _migrate_env_vars()
    if migrated:
        return [migrated]
    return []


def get_account(identifier: str | None = None) -> Optional[dict]:
    """Look up one account. None = primary. Matches id, then provider, then partial address."""
    accounts = get_all_accounts()
    if not accounts:
        return None
    if identifier is None:
        # Return primary
        data = _load_accounts_file()
        primary_id = data.get("primary")
        if primary_id:
            for a in accounts:
                if a["id"] == primary_id:
                    return a
        return accounts[0]
    identifier = identifier.lower()
    # Match by id
    for a in accounts:
        if a["id"].lower() == identifier:
            return a
    # Match by provider
    for a in accounts:
        if a.get("provider", "").lower() == identifier:
            return a
    # Match by partial address
    for a in accounts:
        if identifier in a.get("address", "").lower():
            return a
    return None


def _unique_id(provider_key: str, accounts: list[dict]) -> str:
    """Generate a unique account ID. Appends -2, -3 etc. for duplicate providers."""
    existing_ids = {a["id"] for a in accounts}
    if provider_key not in existing_ids:
        return provider_key
    n = 2
    while f"{provider_key}-{n}" in existing_ids:
        n += 1
    return f"{provider_key}-{n}"


def add_account(account: dict):
    """Upsert an account by id. Sets primary if first. Syncs primary to env vars."""
    data = _load_accounts_file()
    acct_id = account.get("id", "")
    if not acct_id:
        provider = account.get("provider", "custom")
        acct_id = _unique_id(provider, data["accounts"])
        account["id"] = acct_id
    # Upsert by id
    found = False
    for i, existing in enumerate(data["accounts"]):
        if existing["id"] == acct_id:
            data["accounts"][i] = account
            found = True
            break
    # Dedup by email address — update existing if same address
    if not found and account.get("address"):
        for i, existing in enumerate(data["accounts"]):
            if existing.get("address") == account["address"]:
                account["id"] = existing["id"]  # keep original id
                acct_id = account["id"]
                data["accounts"][i] = account
                found = True
                break
    if not found:
        data["accounts"].append(account)
    # Set primary if first account
    if data["primary"] is None or len(data["accounts"]) == 1:
        data["primary"] = acct_id
    _save_accounts_file(data)
    # Sync primary to env vars
    if data["primary"] == acct_id:
        _sync_primary_to_env(account)


def remove_account(account_id: str) -> bool:
    """Remove an account by id. Returns True if found."""
    data = _load_accounts_file()
    original_len = len(data["accounts"])
    data["accounts"] = [a for a in data["accounts"] if a["id"] != account_id]
    if len(data["accounts"]) == original_len:
        return False
    # Fix primary if removed
    if data["primary"] == account_id:
        data["primary"] = data["accounts"][0]["id"] if data["accounts"] else None
        if data["primary"]:
            primary = next((a for a in data["accounts"] if a["id"] == data["primary"]), None)
            if primary:
                _sync_primary_to_env(primary)
    _save_accounts_file(data)
    return True


def account_count() -> int:
    return len(get_all_accounts())


def _sync_primary_to_env(account: dict):
    """Write primary account credentials to os.environ for backward compat."""
    os.environ["EMAIL_ADDRESS"] = account.get("address", "")
    os.environ["EMAIL_PASSWORD"] = account.get("password", "")
    os.environ["EMAIL_IMAP_SERVER"] = account.get("imap_server", "imap.gmail.com")
    os.environ["EMAIL_SMTP_SERVER"] = account.get("smtp_server", "smtp.gmail.com")
    os.environ["EMAIL_IMAP_PORT"] = str(account.get("imap_port", 993))
    os.environ["EMAIL_SMTP_PORT"] = str(account.get("smtp_port", 587))
