"""Avatar chat file drop — MIME routing and workspace upload (0.2.2.24)."""

from __future__ import annotations

import json
import os
import re
import shutil
import zipfile
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from iuve_connect_agents.shared.feature_flags import (
    avatar_chat_file_drop_enabled,
    avatar_desktop_vision_enabled,
)

ALLOWED_EXTENSIONS: frozenset[str] = frozenset(
    {
        ".txt",
        ".md",
        ".pdf",
        ".png",
        ".jpg",
        ".jpeg",
        ".webp",
        ".mp3",
        ".wav",
        ".zip",
        ".py",
        ".ts",
        ".js",
        ".json",
        ".odt",
    }
)

IMAGE_EXTENSIONS = frozenset({".png", ".jpg", ".jpeg", ".webp"})
_IMAGE_PATH_RE = re.compile(
    r"(?P<path>(?:~|/)[^\n\r<>\"']+?\.(?:png|jpe?g|webp))",
    re.IGNORECASE,
)
AUDIO_EXTENSIONS = frozenset({".mp3", ".wav"})
CODE_EXTENSIONS = frozenset({".py", ".ts", ".js", ".json"})
DOCUMENT_EXTENSIONS = frozenset({".txt", ".md", ".pdf", ".odt"})

MIME_CATEGORY: dict[str, str] = {
    "image/png": "vision",
    "image/jpeg": "vision",
    "image/webp": "vision",
    "audio/mpeg": "audio",
    "audio/wav": "audio",
    "audio/x-wav": "audio",
    "application/zip": "archive",
    "application/x-zip-compressed": "archive",
    "text/plain": "document",
    "text/markdown": "document",
    "application/pdf": "document",
    "application/vnd.oasis.opendocument.text": "document",
    "application/json": "code",
    "text/x-python": "code",
    "text/javascript": "code",
    "application/typescript": "code",
}


@dataclass(frozen=True)
class FileDropRoute:
    category: str
    routed_model: str
    advisory_hint: str


@dataclass(frozen=True)
class FileDropResult:
    ok: bool
    filename: str
    stored_path: str
    route: FileDropRoute
    message_key: str
    message_params: dict[str, Any]
    audit_entry: dict[str, Any]


def default_uploads_dir() -> Path:
    workspace = Path.home() / "IuVeWorkspace"
    uploads = workspace / "uploads"
    uploads.mkdir(parents=True, exist_ok=True)
    return uploads


def audit_log_path() -> Path:
    log_dir = Path.home() / ".local/share/iuve-connect"
    log_dir.mkdir(parents=True, exist_ok=True)
    return log_dir / "file-drop-audit.jsonl"


def extension_allowed(path: Path | str) -> bool:
    return Path(path).suffix.lower() in ALLOWED_EXTENSIONS


def extract_image_file_path(text: str) -> str | None:
    """Extract first existing image file path from chat text (paste / path mention)."""
    raw = str(text or "").strip()
    if not raw:
        return None
    for match in _IMAGE_PATH_RE.finditer(raw):
        candidate = Path(match.group("path")).expanduser()
        if candidate.suffix.lower() in IMAGE_EXTENSIONS and candidate.is_file():
            return str(candidate)
    return None


def _vision_question_from_text(text: str, *, image_path: str) -> str:
    cleaned = raw.replace(image_path, " ").strip() if (raw := str(text or "")) else ""
    for token in (
        "проанализируй",
        "анализируй",
        "посмотри",
        "что на",
        "что изображено",
        "analyze",
        "look at",
        "what is on",
        "what's on",
    ):
        if token in cleaned.lower():
            return cleaned
    return cleaned or "visible UI content on screenshot"


def build_image_path_vision_plan(request_text: str, *, image_path: str) -> dict[str, Any] | None:
    """Plan verify step against an on-disk screenshot path (workspace / paste)."""
    src = Path(image_path).expanduser()
    if not src.is_file() or src.suffix.lower() not in IMAGE_EXTENSIONS:
        return None

    drop = process_dropped_file(src)
    stored_path = drop.stored_path or str(src)
    question = _vision_question_from_text(request_text, image_path=image_path)
    return {
        "goal_summary": "Analyze provided screenshot image",
        "steps": [
            {
                "step_id": "s1",
                "command_type": "verify_screen_contains_text",
                "payload": {
                    "screenshot_path": stored_path,
                    "capture_if_missing": False,
                    "expected_text": question[:240] or "visible content",
                    "match_mode": "contains",
                    "min_confidence": 0.35,
                    "screen_vision_granted": True,
                    "user_consent_state": "consented_via_connect_flow",
                },
            }
        ],
    }


def route_mime(*, filename: str, mime_type: str = "") -> FileDropRoute:
    ext = Path(filename).suffix.lower()
    mime = str(mime_type or "").strip().lower()

    category = MIME_CATEGORY.get(mime, "")
    if not category:
        if ext in IMAGE_EXTENSIONS:
            category = "vision"
        elif ext in AUDIO_EXTENSIONS:
            category = "audio"
        elif ext in CODE_EXTENSIONS:
            category = "code"
        elif ext in DOCUMENT_EXTENSIONS:
            category = "document"
        elif ext == ".zip":
            category = "archive"
        else:
            category = "document"

    if category == "vision":
        if avatar_desktop_vision_enabled():
            return FileDropRoute(
                category="vision",
                routed_model="vision-local",
                advisory_hint="vision_analyze",
            )
        return FileDropRoute(
            category="document",
            routed_model="orchestra-advisory",
            advisory_hint="document_advisory",
        )
    if category == "audio":
        return FileDropRoute(
            category="audio",
            routed_model="orchestra-advisory",
            advisory_hint="audio_transcription_planned",
        )
    if category == "code":
        return FileDropRoute(
            category="code",
            routed_model="coding-provider",
            advisory_hint="coding_hint",
        )
    if category == "archive":
        return FileDropRoute(
            category="archive",
            routed_model="local-workspace",
            advisory_hint="zip_listing",
        )
    return FileDropRoute(
        category="document",
        routed_model="orchestra-advisory",
        advisory_hint="document_advisory",
    )


def _append_audit(entry: dict[str, Any]) -> None:
    path = audit_log_path()
    line = json.dumps(entry, ensure_ascii=True, sort_keys=True)
    with path.open("a", encoding="utf-8") as handle:
        handle.write(line + "\n")
    try:
        os.chmod(path, 0o600)
    except OSError:
        pass


def _unique_dest(uploads_dir: Path, filename: str) -> Path:
    base = Path(filename).name
    dest = uploads_dir / base
    if not dest.exists():
        return dest
    stem = dest.stem
    suffix = dest.suffix
    counter = 1
    while True:
        candidate = uploads_dir / f"{stem}-{counter}{suffix}"
        if not candidate.exists():
            return candidate
        counter += 1


def _zip_listing(path: Path, *, limit: int = 20) -> list[str]:
    names: list[str] = []
    try:
        with zipfile.ZipFile(path) as archive:
            for info in archive.infolist()[:limit]:
                names.append(info.filename)
            if len(archive.infolist()) > limit:
                names.append(f"… +{len(archive.infolist()) - limit} more")
    except Exception as exc:
        names.append(f"(unreadable: {exc})")
    return names


def process_dropped_file(
    source_path: Path | str,
    *,
    mime_type: str = "",
    uploads_dir: Path | None = None,
) -> FileDropResult:
    if not avatar_chat_file_drop_enabled():
        return FileDropResult(
            ok=False,
            filename="",
            stored_path="",
            route=FileDropRoute("disabled", "none", ""),
            message_key="avatar.chat.file_drop.disabled",
            message_params={},
            audit_entry={},
        )

    src = Path(source_path).expanduser()
    if not src.is_file():
        return FileDropResult(
            ok=False,
            filename=src.name,
            stored_path="",
            route=FileDropRoute("error", "none", ""),
            message_key="avatar.chat.file_drop.not_found",
            message_params={"filename": src.name},
            audit_entry={},
        )

    if not extension_allowed(src):
        return FileDropResult(
            ok=False,
            filename=src.name,
            stored_path="",
            route=FileDropRoute("rejected", "none", ""),
            message_key="avatar.chat.file_drop.unsupported",
            message_params={"filename": src.name},
            audit_entry={},
        )

    target_dir = uploads_dir or default_uploads_dir()
    target_dir.mkdir(parents=True, exist_ok=True)
    dest = _unique_dest(target_dir, src.name)
    shutil.copy2(src, dest)
    route = route_mime(filename=dest.name, mime_type=mime_type)

    message_params: dict[str, Any] = {
        "filename": dest.name,
        "routed_model": route.routed_model,
    }
    message_key = "avatar.chat.file_drop.chip"

    if route.advisory_hint == "audio_transcription_planned":
        message_key = "avatar.chat.file_drop.audio_stub"
    elif route.advisory_hint == "zip_listing":
        listing = _zip_listing(dest)
        message_params["listing"] = ", ".join(listing) if listing else "(empty)"
        message_key = "avatar.chat.file_drop.zip_listing"
    elif route.advisory_hint == "coding_hint":
        message_key = "avatar.chat.file_drop.code_hint"

    audit_entry = {
        "ts": datetime.now(timezone.utc).isoformat(),
        "action": "avatar_file_drop",
        "source": str(src),
        "dest": str(dest),
        "mime": mime_type or "",
        "category": route.category,
        "routed_model": route.routed_model,
    }
    _append_audit(audit_entry)

    return FileDropResult(
        ok=True,
        filename=dest.name,
        stored_path=str(dest),
        route=route,
        message_key=message_key,
        message_params=message_params,
        audit_entry=audit_entry,
    )
