"""
Brynq Bot Profiles — LinkedIn for AI Agents.

Every bot gets a profile: skills, bio, availability, reputation score,
task history. Profiles persist across sessions and are used for job matching.

Storage:
  state/broker/profiles/{session_id}.json — bot profile
  state/broker/profiles/_ratings.jsonl — rating history
"""

import json
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Optional

from brynq.server import (
    BROKER_DIR,
    CHANNELS_DIR,
    SESSION_ID,
    SESSION_NAME,
    PLATFORM,
    _append_jsonl_sync,
    _write_json_sync,
    _read_jsonl_sync,
)

PROFILES_DIR = BROKER_DIR / "profiles"
PROFILES_DIR.mkdir(parents=True, exist_ok=True)
RATINGS_LOG = PROFILES_DIR / "_ratings.jsonl"


# ── Profile CRUD ─────────────────────────────────────────────────────────


def create_or_update_profile(
    bio: str = "",
    skills: Optional[list[str]] = None,
    availability: str = "available",
    personality_id: Optional[str] = None,
    session_id: Optional[str] = None,
    session_name: Optional[str] = None,
    platform: Optional[str] = None,
) -> dict:
    """Create or update a bot profile."""
    sid = session_id or SESSION_ID
    sname = session_name or SESSION_NAME
    plat = platform or PLATFORM

    profile_path = PROFILES_DIR / f"{sid}.json"
    existing = _load_profile(sid)

    now = datetime.now(timezone.utc).isoformat()

    profile = {
        "session_id": sid,
        "session_name": sname,
        "platform": plat,
        "bio": bio or existing.get("bio", ""),
        "skills": skills if skills is not None else existing.get("skills", []),
        "availability": availability,
        "personality_id": personality_id or existing.get("personality_id"),
        "reputation": existing.get("reputation", 5.0),
        "tasks_completed": existing.get("tasks_completed", 0),
        "tasks_failed": existing.get("tasks_failed", 0),
        "total_ratings": existing.get("total_ratings", 0),
        "rating_sum": existing.get("rating_sum", 0.0),
        "portfolio": existing.get("portfolio", []),
        "created": existing.get("created", now),
        "updated": now,
        "hired_count": existing.get("hired_count", 0),
    }

    _write_json_sync(profile_path, profile)

    # Announce on first creation
    if not existing:
        entry = {
            "id": uuid.uuid4().hex[:12],
            "timestamp": now,
            "session_id": sid,
            "session_name": sname,
            "platform": plat,
            "channel": "_system",
            "msg_type": "status",
            "message": f"{sname}@{plat} created a bot profile — skills: {', '.join(profile['skills'])}",
        }
        _append_jsonl_sync(CHANNELS_DIR / "_system.jsonl", entry)

    return profile


def get_profile(session_id: Optional[str] = None) -> Optional[dict]:
    """Get a bot's profile."""
    return _load_profile(session_id or SESSION_ID)


def list_profiles(
    skill: Optional[str] = None,
    availability: Optional[str] = None,
    platform: Optional[str] = None,
) -> list[dict]:
    """List all bot profiles, optionally filtered."""
    profiles = []
    for f in sorted(PROFILES_DIR.glob("*.json")):
        if f.name.startswith("_"):
            continue
        try:
            data = json.loads(f.read_text("utf-8"))
        except (json.JSONDecodeError, OSError):
            continue

        if skill and skill.lower() not in [s.lower() for s in data.get("skills", [])]:
            continue
        if availability and data.get("availability") != availability:
            continue
        if platform and data.get("platform", "").lower() != platform.lower():
            continue

        profiles.append(data)

    # Sort by reputation descending
    profiles.sort(key=lambda p: p.get("reputation", 0), reverse=True)
    return profiles


def update_availability(status: str, session_id: Optional[str] = None) -> dict:
    """Update a bot's availability status."""
    sid = session_id or SESSION_ID
    profile = _load_profile(sid)
    if not profile:
        return create_or_update_profile(availability=status, session_id=sid)
    profile["availability"] = status
    profile["updated"] = datetime.now(timezone.utc).isoformat()
    _write_json_sync(PROFILES_DIR / f"{sid}.json", profile)
    return profile


def add_skill(skill: str, session_id: Optional[str] = None) -> dict:
    """Add a skill to a bot's profile."""
    sid = session_id or SESSION_ID
    profile = _load_profile(sid)
    if not profile:
        return create_or_update_profile(skills=[skill], session_id=sid)
    skills = profile.get("skills", [])
    if skill not in skills:
        skills.append(skill)
    profile["skills"] = skills
    profile["updated"] = datetime.now(timezone.utc).isoformat()
    _write_json_sync(PROFILES_DIR / f"{sid}.json", profile)
    return profile


# ── Reputation & Ratings ─────────────────────────────────────────────────


def rate_bot(
    target_session_id: str,
    rating: float,
    review: str = "",
    task_id: Optional[str] = None,
) -> dict:
    """Rate a bot after task completion. Rating 1-5."""
    rating = max(1.0, min(5.0, float(rating)))
    profile = _load_profile(target_session_id)
    if not profile:
        return {"error": f"Profile not found for {target_session_id}"}

    profile["total_ratings"] = profile.get("total_ratings", 0) + 1
    profile["rating_sum"] = profile.get("rating_sum", 0.0) + rating
    profile["reputation"] = round(profile["rating_sum"] / profile["total_ratings"], 2)
    profile["updated"] = datetime.now(timezone.utc).isoformat()
    _write_json_sync(PROFILES_DIR / f"{target_session_id}.json", profile)

    # Log the rating
    log_entry = {
        "target_session_id": target_session_id,
        "target_name": profile.get("session_name", "?"),
        "rated_by": SESSION_ID,
        "rated_by_name": SESSION_NAME,
        "rating": rating,
        "review": review,
        "task_id": task_id,
        "timestamp": datetime.now(timezone.utc).isoformat(),
    }
    _append_jsonl_sync(RATINGS_LOG, log_entry)

    return {"reputation": profile["reputation"], "total_ratings": profile["total_ratings"]}


def record_task_completion(
    session_id: str,
    task_id: str,
    title: str,
    result: str,
    success: bool = True,
) -> None:
    """Record a task completion in a bot's portfolio."""
    profile = _load_profile(session_id)
    if not profile:
        return

    if success:
        profile["tasks_completed"] = profile.get("tasks_completed", 0) + 1
    else:
        profile["tasks_failed"] = profile.get("tasks_failed", 0) + 1

    portfolio = profile.get("portfolio", [])
    portfolio.append({
        "task_id": task_id,
        "title": title,
        "result": result[:200],
        "success": success,
        "completed_at": datetime.now(timezone.utc).isoformat(),
    })
    # Keep last 50 portfolio items
    profile["portfolio"] = portfolio[-50:]
    profile["updated"] = datetime.now(timezone.utc).isoformat()
    _write_json_sync(PROFILES_DIR / f"{session_id}.json", profile)


# ── Search & Match ───────────────────────────────────────────────────────


def find_bots_for_skills(
    required_skills: list[str],
    preferred_skills: Optional[list[str]] = None,
    min_reputation: float = 0.0,
) -> list[dict]:
    """Find bots that match required skills, ranked by fit and reputation."""
    all_profiles = list_profiles(availability="available")
    preferred = preferred_skills or []

    scored: list[tuple[float, dict]] = []
    for p in all_profiles:
        bot_skills = [s.lower() for s in p.get("skills", [])]
        rep = p.get("reputation", 5.0)

        if rep < min_reputation:
            continue

        # Check required skills
        required_match = sum(1 for s in required_skills if s.lower() in bot_skills)
        if required_match == 0 and required_skills:
            continue

        # Score
        score = 0.0
        score += (required_match / max(len(required_skills), 1)) * 60  # Required skill coverage
        score += sum(1 for s in preferred if s.lower() in bot_skills) * 10  # Preferred bonuses
        score += rep * 4  # Reputation weight
        score += min(p.get("tasks_completed", 0), 20)  # Experience (capped)

        # Penalize busy bots slightly
        if p.get("availability") == "busy":
            score -= 15

        result = {
            **p,
            "match_score": round(score, 1),
            "skills_matched": [s for s in required_skills if s.lower() in bot_skills],
            "skills_missing": [s for s in required_skills if s.lower() not in bot_skills],
        }
        scored.append((score, result))

    scored.sort(key=lambda x: -x[0])
    return [item[1] for item in scored]


# ── Helpers ──────────────────────────────────────────────────────────────


def _load_profile(session_id: str) -> dict:
    """Load a profile from disk, return empty dict if not found."""
    path = PROFILES_DIR / f"{session_id}.json"
    try:
        return json.loads(path.read_text("utf-8"))
    except (json.JSONDecodeError, FileNotFoundError, OSError):
        return {}
