# 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

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

Stores accounts in ~/.familiar/data/calendar_accounts.json (chmod 600).
Provides transparent migration from legacy google_token.json and CalDAV env vars.

Account types:
  - "google": token_file (relative to DATA_DIR), calendar_id
  - "caldav": url, user, password
"""

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

logger = logging.getLogger(__name__)


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() / "calendar_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 calendar 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)


# ---------------------------------------------------------------------------
# Migration helpers
# ---------------------------------------------------------------------------


def _migrate_google_token() -> Optional[dict]:
    """If google_token.json exists in DATA_DIR, build a Google account dict."""
    data_dir = _data_dir()
    token_path = data_dir / "google_token.json"
    if not token_path.exists():
        return None
    return {
        "id": "google",
        "type": "google",
        "label": "",
        "token_file": "google_token.json",
        "calendar_id": "primary",
    }


def _migrate_caldav_env() -> Optional[dict]:
    """If CALDAV_URL + CALDAV_USER + CALDAV_PASSWORD are set, build a CalDAV account."""
    url = os.environ.get("CALDAV_URL", "")
    user = os.environ.get("CALDAV_USER", "")
    password = os.environ.get("CALDAV_PASSWORD", "")
    if not (url and user and password):
        return None
    return {
        "id": "caldav",
        "type": "caldav",
        "label": "",
        "url": url,
        "user": user,
        "password": password,
    }


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


def get_all_accounts() -> list[dict]:
    """Load all accounts, auto-migrating from token file / env vars if registry is empty."""
    data = _load_accounts_file()
    if data["accounts"]:
        return data["accounts"]
    # Auto-migrate: Google first, then CalDAV
    migrated = []
    google = _migrate_google_token()
    if google:
        migrated.append(google)
    caldav = _migrate_caldav_env()
    if caldav:
        migrated.append(caldav)
    return migrated


def get_account(identifier: str | None = None) -> Optional[dict]:
    """Look up one account. None = primary. Matches id, then type, then label (partial, case-insensitive)."""
    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 type
    for a in accounts:
        if a.get("type", "").lower() == identifier:
            return a
    # Match by label (partial, case-insensitive)
    for a in accounts:
        if identifier in a.get("label", "").lower():
            return a
    return None


def _unique_id(type_key: str, accounts: list[dict]) -> str:
    """Generate a unique account ID. Appends -2, -3 etc. for duplicate types."""
    existing_ids = {a["id"] for a in accounts}
    if type_key not in existing_ids:
        return type_key
    n = 2
    while f"{type_key}-{n}" in existing_ids:
        n += 1
    return f"{type_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:
        type_key = account.get("type", "custom")
        acct_id = _unique_id(type_key, data["accounts"])
        account["id"] = acct_id
    # Upsert
    found = False
    for i, existing in enumerate(data["accounts"]):
        if existing["id"] == acct_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."""
    acct_type = account.get("type", "")
    if acct_type == "caldav":
        os.environ["CALDAV_URL"] = account.get("url", "")
        os.environ["CALDAV_USER"] = account.get("user", "")
        os.environ["CALDAV_PASSWORD"] = account.get("password", "")
        os.environ["CALENDAR_PROVIDER"] = "caldav"
    elif acct_type == "google":
        os.environ["CALENDAR_PROVIDER"] = "google"
