# 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

"""Social platform wizards — Mastodon / GoToSocial and Bluesky."""

from __future__ import annotations

import logging
import os
from pathlib import Path

from ..formatting import fmt_bold, fmt_code, fmt_italic
from ._helpers import _check_tcp_port, _service_doc_blurb, _test_http_service, _update_env_file
from ._protocol import WizardHost

logger = logging.getLogger(__name__)


# ── Mastodon / GoToSocial ────────────────────────────────────────


async def wizard_mastodon(host: WizardHost, recipient_id: str, args: list) -> None:
    """Entry point for /connect mastodon."""
    m = host.format_mode
    sub = args[0] if args else ""

    if sub == "test":
        await wizard_mastodon_test(host, recipient_id)
        return

    has_url = os.environ.get("MASTODON_INSTANCE_URL", "")
    has_token = bool(os.environ.get("MASTODON_ACCESS_TOKEN"))

    if has_url and has_token:
        await host.wizard_send(
            recipient_id,
            f"# {fmt_bold('Mastodon Configuration', m)}\n\n"
            f"  Instance: {fmt_code(has_url, m)}\n"
            f"  Token: ********\n\n"
            f"{fmt_bold('Commands:', m)}\n"
            f"  {fmt_code('/connect mastodon test', m)} — test connection\n",
        )
        return

    blurb = _service_doc_blurb("mastodon", m)

    # Auto-detect local GoToSocial
    local_hint = ""
    if _check_tcp_port("localhost", 8081):
        local_hint = (
            f"\n{fmt_bold('Local GoToSocial detected on port 8081!', m)}\n"
            f"You can use {fmt_code('http://localhost:8081', m)} as the instance URL.\n"
        )

    host._ensure_wizard_state()
    host._wizard_state[recipient_id] = {"type": "mastodon", "step": "instance_url"}

    await host.wizard_send(
        recipient_id,
        f"# {fmt_bold('Connect Mastodon / GoToSocial', m)}\n"
        f"{blurb}"
        f"{local_hint}\n"
        f"{fmt_bold('Setup:', m)}\n"
        f"1. Go to your instance Preferences > Development > New Application\n"
        f"2. Create an application with read+write scopes\n"
        f"3. Copy the access token\n\n"
        f"Enter your instance URL (e.g. {fmt_code('https://mastodon.social', m)}):",
    )


async def mastodon_handle_step(
    host: WizardHost, recipient_id: str, text: str, message_ref
) -> None:
    """Multi-step wizard handler for Mastodon."""
    host._ensure_wizard_state()
    state = host._wizard_state.get(recipient_id, {})
    step = state.get("step", "")
    m = host.format_mode

    if step == "instance_url":
        url = text.strip().rstrip("/")
        if not url.startswith("http"):
            url = f"https://{url}"

        host._wizard_state[recipient_id] = {
            "type": "mastodon",
            "step": "access_token",
            "instance_url": url,
        }

        warning = ""
        if not host.supports_message_deletion:
            warning = f"\n\n{fmt_italic('Token will remain visible in chat.', m)}"

        await host.wizard_send(
            recipient_id,
            f"Instance: {fmt_code(url, m)}\n\n"
            f"Now paste your {fmt_bold('access token', m)}:{warning}",
        )

    elif step == "access_token":
        token = text.strip()
        url = state.get("instance_url", "")

        # Delete token message if possible
        if host.supports_message_deletion and message_ref:
            await host.wizard_delete_message(recipient_id, message_ref)

        # Save to env
        os.environ["MASTODON_INSTANCE_URL"] = url
        os.environ["MASTODON_ACCESS_TOKEN"] = token
        env_path = Path.home() / ".familiar" / ".env"
        try:
            _update_env_file(env_path, {
                "MASTODON_INSTANCE_URL": url,
                "MASTODON_ACCESS_TOKEN": token,
            })
        except Exception as e:
            await host.wizard_send(recipient_id, f"Failed to save: {e}")
            return

        # Clear wizard state
        host._wizard_state.pop(recipient_id, None)

        await host.wizard_send(recipient_id, "Testing connection...")
        await wizard_mastodon_test(host, recipient_id)


async def wizard_mastodon_test(host: WizardHost, recipient_id: str) -> None:
    """Test Mastodon connection by verifying credentials."""
    url = os.environ.get("MASTODON_INSTANCE_URL", "").rstrip("/")
    token = os.environ.get("MASTODON_ACCESS_TOKEN", "")
    m = host.format_mode

    if not url or not token:
        await host.wizard_send(
            recipient_id,
            "Mastodon not configured. Run /connect mastodon to set up.",
        )
        return

    ok, msg = _test_http_service(
        f"{url}/api/v1/accounts/verify_credentials",
        headers={"Authorization": f"Bearer {token}"},
    )

    if ok:
        await host.wizard_send(
            recipient_id,
            f"Mastodon connected!\n"
            f"  Instance: {fmt_code(url, m)}\n"
            f"  Status: {msg}",
        )
    else:
        await host.wizard_send(
            recipient_id,
            f"Connection failed\n  {msg}",
        )


# ── Bluesky / AT Protocol ───────────────────────────────────────


async def wizard_bluesky(host: WizardHost, recipient_id: str, args: list) -> None:
    """Entry point for /connect bluesky."""
    m = host.format_mode
    sub = args[0] if args else ""

    if sub == "test":
        await wizard_bluesky_test(host, recipient_id)
        return

    has_handle = os.environ.get("BLUESKY_HANDLE", "")
    has_password = bool(os.environ.get("BLUESKY_APP_PASSWORD"))

    if has_handle and has_password:
        pds = os.environ.get("BLUESKY_PDS_URL", "https://bsky.social")
        await host.wizard_send(
            recipient_id,
            f"# {fmt_bold('Bluesky Configuration', m)}\n\n"
            f"  Handle: {fmt_code(has_handle, m)}\n"
            f"  PDS: {fmt_code(pds, m)}\n"
            f"  Password: ********\n\n"
            f"{fmt_bold('Commands:', m)}\n"
            f"  {fmt_code('/connect bluesky test', m)} — test connection\n",
        )
        return

    blurb = _service_doc_blurb("bluesky", m)

    host._ensure_wizard_state()
    host._wizard_state[recipient_id] = {"type": "bluesky", "step": "handle"}

    await host.wizard_send(
        recipient_id,
        f"# {fmt_bold('Connect Bluesky', m)}\n"
        f"{blurb}\n"
        f"{fmt_bold('Setup:', m)}\n"
        f"1. Go to Settings > App Passwords in Bluesky\n"
        f"2. Create a new app password\n"
        f"3. Copy the generated password\n\n"
        f"Enter your Bluesky handle (e.g. {fmt_code('yourname.bsky.social', m)}):",
    )


async def bluesky_handle_step(
    host: WizardHost, recipient_id: str, text: str, message_ref
) -> None:
    """Multi-step wizard handler for Bluesky."""
    host._ensure_wizard_state()
    state = host._wizard_state.get(recipient_id, {})
    step = state.get("step", "")
    m = host.format_mode

    if step == "handle":
        handle = text.strip().lstrip("@")

        host._wizard_state[recipient_id] = {
            "type": "bluesky",
            "step": "app_password",
            "handle": handle,
        }

        warning = ""
        if not host.supports_message_deletion:
            warning = f"\n\n{fmt_italic('Password will remain visible in chat.', m)}"

        await host.wizard_send(
            recipient_id,
            f"Handle: {fmt_code(handle, m)}\n\n"
            f"Now paste your {fmt_bold('app password', m)}:{warning}",
        )

    elif step == "app_password":
        password = text.strip()
        handle = state.get("handle", "")

        # Delete password message if possible
        if host.supports_message_deletion and message_ref:
            await host.wizard_delete_message(recipient_id, message_ref)

        host._wizard_state[recipient_id] = {
            "type": "bluesky",
            "step": "pds_url",
            "handle": handle,
            "app_password": password,
        }

        # Auto-detect local PDS
        local_hint = ""
        if _check_tcp_port("localhost", 2583):
            local_hint = (
                f"\n{fmt_bold('Local PDS detected on port 2583!', m)}\n"
                f"Type {fmt_code('local', m)} to use it.\n"
            )

        await host.wizard_send(
            recipient_id,
            f"Enter your PDS URL, or press Enter for {fmt_code('https://bsky.social', m)}:"
            f"{local_hint}\n"
            f"(Type {fmt_code('skip', m)} or just press Enter for the default)",
        )

    elif step == "pds_url":
        handle = state.get("handle", "")
        password = state.get("app_password", "")
        pds_input = text.strip().lower()

        if pds_input in ("", "skip", "default"):
            pds_url = "https://bsky.social"
        elif pds_input == "local":
            pds_url = "http://localhost:2583"
        else:
            pds_url = text.strip().rstrip("/")
            if not pds_url.startswith("http"):
                pds_url = f"https://{pds_url}"

        # Save to env
        os.environ["BLUESKY_HANDLE"] = handle
        os.environ["BLUESKY_APP_PASSWORD"] = password
        os.environ["BLUESKY_PDS_URL"] = pds_url
        env_path = Path.home() / ".familiar" / ".env"
        try:
            _update_env_file(env_path, {
                "BLUESKY_HANDLE": handle,
                "BLUESKY_APP_PASSWORD": password,
                "BLUESKY_PDS_URL": pds_url,
            })
        except Exception as e:
            await host.wizard_send(recipient_id, f"Failed to save: {e}")
            return

        # Clear wizard state
        host._wizard_state.pop(recipient_id, None)

        await host.wizard_send(recipient_id, "Testing connection...")
        await wizard_bluesky_test(host, recipient_id)


async def wizard_bluesky_test(host: WizardHost, recipient_id: str) -> None:
    """Test Bluesky connection by creating a session."""
    handle = os.environ.get("BLUESKY_HANDLE", "")
    password = os.environ.get("BLUESKY_APP_PASSWORD", "")
    pds_url = os.environ.get("BLUESKY_PDS_URL", "https://bsky.social").rstrip("/")
    m = host.format_mode

    if not handle or not password:
        await host.wizard_send(
            recipient_id,
            "Bluesky not configured. Run /connect bluesky to set up.",
        )
        return

    import httpx

    try:
        resp = httpx.post(
            f"{pds_url}/xrpc/com.atproto.server.createSession",
            json={"identifier": handle, "password": password},
            timeout=10,
        )
        if resp.status_code == 200:
            data = resp.json()
            did = data.get("did", "")
            await host.wizard_send(
                recipient_id,
                f"Bluesky connected!\n"
                f"  Handle: {fmt_code(handle, m)}\n"
                f"  DID: {fmt_code(did, m)}\n"
                f"  PDS: {fmt_code(pds_url, m)}",
            )
        else:
            await host.wizard_send(
                recipient_id,
                f"Connection failed (HTTP {resp.status_code})\n"
                f"  {resp.text[:200]}",
            )
    except Exception as e:
        await host.wizard_send(
            recipient_id,
            f"Connection failed: {e}",
        )
