"""Admin panel routes (password-protected) - User Management & Statistics."""
import asyncio
import re
import secrets
import math
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional
from uuid import UUID
from collections import defaultdict

from fastapi import APIRouter, Request, Form, HTTPException
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
from loguru import logger

from src.database import db
from src.web.admin.auth import (
    WEB_PASSWORD, AUTH_COOKIE_NAME, hash_password, verify_auth
)
from src.web.user.auth import UserSession, set_user_session
from src.config import settings
from src.services import moco_service

# Router with /admin prefix
admin_router = APIRouter(prefix="/admin", tags=["admin"])

# Templates
templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates" / "admin")


# ==================== AUTH ROUTES ====================

@admin_router.get("/login", response_class=HTMLResponse)
async def login_page(request: Request, error: str = None):
    """Admin login page."""
    if not WEB_PASSWORD:
        return RedirectResponse(url="/admin", status_code=302)
    if verify_auth(request):
        return RedirectResponse(url="/admin", status_code=302)
    return templates.TemplateResponse("login.html", {
        "request": request,
        "error": error
    })


@admin_router.post("/login")
async def login(request: Request, password: str = Form(...)):
    """Handle admin login."""
    if password == WEB_PASSWORD:
        response = RedirectResponse(url="/admin", status_code=302)
        response.set_cookie(
            key=AUTH_COOKIE_NAME,
            value=hash_password(WEB_PASSWORD),
            httponly=True,
            max_age=60 * 60 * 24 * 7,
            samesite="lax"
        )
        return response
    return RedirectResponse(url="/admin/login?error=invalid", status_code=302)


@admin_router.get("/logout")
async def logout():
    """Handle admin logout."""
    response = RedirectResponse(url="/admin/login", status_code=302)
    response.delete_cookie(AUTH_COOKIE_NAME)
    return response


# ==================== IMPERSONATE (enhanced for all account types) ====================

@admin_router.get("/impersonate/{user_id}")
async def impersonate_user(request: Request, user_id: UUID):
    """Login as any user type (ghostwriter, company owner, employee).

    Creates a user session for the given user and redirects to their dashboard.
    Only accessible by authenticated admins.
    """
    if not verify_auth(request):
        return RedirectResponse(url="/admin/login", status_code=302)

    try:
        # Get profile to determine account type
        profile = await db.get_profile(user_id)
        if not profile:
            raise HTTPException(status_code=404, detail="User not found")

        user = await db.get_user(user_id)
        account_type = profile.account_type.value if hasattr(profile.account_type, 'value') else profile.account_type

        # Build session based on account type
        if account_type == "company":
            # Company owner
            company = await db.get_company_by_owner(user_id) if not profile.company_id else await db.get_company(profile.company_id)
            session = UserSession(
                user_id=str(user_id),
                linkedin_vanity_name="",
                linkedin_name=profile.display_name or (user.email if user else ""),
                linkedin_picture=None,
                email=user.email if user else None,
                account_type="company",
                company_id=str(company.id) if company else (str(profile.company_id) if profile.company_id else None),
                onboarding_status=profile.onboarding_status.value if hasattr(profile.onboarding_status, 'value') else profile.onboarding_status,
                company_name=company.name if company else None,
                display_name=profile.display_name
            )
            response = RedirectResponse(url="/company/manage", status_code=302)

        elif account_type == "employee":
            # Employee
            company = await db.get_company(profile.company_id) if profile.company_id else None

            linkedin_vanity = ""
            if profile.linkedin_url:
                match = re.search(r'linkedin\.com/in/([^/?]+)', profile.linkedin_url)
                if match:
                    linkedin_vanity = match.group(1)

            session = UserSession(
                user_id=str(user_id),
                linkedin_vanity_name=linkedin_vanity,
                linkedin_name=profile.display_name or "",
                linkedin_picture=profile.profile_picture,
                email=user.email if user else None,
                account_type="employee",
                company_id=str(profile.company_id) if profile.company_id else None,
                onboarding_status=profile.onboarding_status.value if hasattr(profile.onboarding_status, 'value') else profile.onboarding_status,
                company_name=company.name if company else None,
                display_name=profile.display_name
            )
            response = RedirectResponse(url="/", status_code=302)

        else:
            # Ghostwriter (default)
            linkedin_vanity = ""
            if profile.linkedin_url:
                match = re.search(r'linkedin\.com/in/([^/?]+)', profile.linkedin_url)
                if match:
                    linkedin_vanity = match.group(1)

            session = UserSession(
                user_id=str(user_id),
                linkedin_vanity_name=linkedin_vanity or (profile.display_name or "").lower().replace(" ", "-"),
                linkedin_name=profile.display_name or "",
                linkedin_picture=profile.profile_picture,
                email=user.email if user else None,
                account_type="ghostwriter",
                onboarding_status=profile.onboarding_status.value if hasattr(profile.onboarding_status, 'value') else profile.onboarding_status,
                display_name=profile.display_name
            )
            response = RedirectResponse(url="/", status_code=302)

        set_user_session(response, session)
        return response

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error impersonating user: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ==================== USER MANAGEMENT (main admin page) ====================

@admin_router.get("", response_class=HTMLResponse)
@admin_router.get("/", response_class=HTMLResponse)
async def home(request: Request):
    """Admin dashboard - User Management."""
    if not verify_auth(request):
        return RedirectResponse(url="/admin/login", status_code=302)
    try:
        # Get all users
        all_users = await db.list_users()

        # Get all companies
        companies = await db.list_companies()
        company_map = {str(c.id): c for c in companies}

        # Build company data with owner + employees
        company_data = []
        for company in companies:
            owner = next((u for u in all_users if str(u.id) == str(company.owner_user_id)), None)
            employee_users = await db.list_users(account_type="employee", company_id=company.id)
            company_data.append({
                "company": company,
                "owner": owner,
                "employees": employee_users,
                "employee_count": len(employee_users)
            })

        # Get ghostwriters
        ghostwriters = await db.list_users(account_type="ghostwriter")

        # Summary stats
        total_users = len(all_users)
        total_companies = len(companies)
        total_ghostwriters = len(ghostwriters)
        total_employees = sum(cd["employee_count"] for cd in company_data)

        return templates.TemplateResponse("dashboard.html", {
            "request": request,
            "page": "users",
            "total_users": total_users,
            "total_companies": total_companies,
            "total_ghostwriters": total_ghostwriters,
            "total_employees": total_employees,
            "company_data": company_data,
            "ghostwriters": ghostwriters,
            "company_map": company_map,
        })
    except Exception as e:
        logger.error(f"Error loading admin dashboard: {e}")
        return templates.TemplateResponse("dashboard.html", {
            "request": request,
            "page": "users",
            "error": str(e),
            "total_users": 0,
            "total_companies": 0,
            "total_ghostwriters": 0,
            "total_employees": 0,
            "company_data": [],
            "ghostwriters": [],
            "company_map": {},
        })


# ==================== USER DELETE API ====================

@admin_router.delete("/api/users/{user_id}")
async def delete_user(request: Request, user_id: UUID):
    """Delete a user and all related data."""
    if not verify_auth(request):
        raise HTTPException(status_code=401, detail="Not authenticated")
    try:
        await db.delete_user_completely(user_id)
        return {"success": True, "message": f"User {user_id} deleted"}
    except Exception as e:
        logger.error(f"Error deleting user {user_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ==================== STATISTICS ====================

@admin_router.get("/statistics", response_class=HTMLResponse)
async def statistics_page(request: Request):
    """Statistics page with usage charts."""
    if not verify_auth(request):
        return RedirectResponse(url="/admin/login", status_code=302)

    try:
        # Get all users for the user selector
        all_users = await db.list_users()
        companies = await db.list_companies()

        # Build company data with employees for the dropdown
        company_data = []
        for company in companies:
            owner = next((u for u in all_users if str(u.id) == str(company.owner_user_id)), None)
            employees = await db.list_users(account_type="employee", company_id=company.id)
            company_data.append({
                "company": company,
                "owner": owner,
                "employees": employees,
            })

        # Get ghostwriters
        ghostwriters = await db.list_users(account_type="ghostwriter")

        return templates.TemplateResponse("statistics.html", {
            "request": request,
            "page": "statistics",
            "users": all_users,
            "companies": company_data,
            "ghostwriters": ghostwriters,
        })
    except Exception as e:
        logger.error(f"Error loading statistics: {e}")
        return templates.TemplateResponse("statistics.html", {
            "request": request,
            "page": "statistics",
            "error": str(e),
            "users": [],
            "companies": [],
            "ghostwriters": [],
        })


@admin_router.get("/api/statistics")
async def get_statistics(request: Request, period: str = "month", user_id: str = None, company_id: str = None):
    """Get statistics data as JSON for charts."""
    if not verify_auth(request):
        raise HTTPException(status_code=401, detail="Not authenticated")

    try:
        now = datetime.utcnow()

        if period == "today":
            start_date = now.replace(hour=0, minute=0, second=0, microsecond=0).isoformat()
        elif period == "week":
            start_date = (now - timedelta(days=7)).isoformat()
        elif period == "month":
            start_date = (now - timedelta(days=30)).isoformat()
        else:  # "all"
            start_date = None

        uid = UUID(user_id) if user_id else None
        cid = UUID(company_id) if company_id else None

        # Get raw logs (table may not exist yet)
        try:
            logs = await db.get_usage_stats(
                start_date=start_date,
                user_id=uid,
                company_id=cid
            )
        except Exception:
            # Table doesn't exist yet - return empty data
            return {
                "total_tokens": 0, "total_cost": 0, "total_calls": 0,
                "daily": [], "by_model": [], "by_user": [], "by_operation": [],
                "period": period
            }

        # Aggregate totals
        total_tokens = sum(l.get("total_tokens", 0) for l in logs)
        total_cost = sum(float(l.get("estimated_cost_usd", 0)) for l in logs)
        total_calls = len(logs)

        # Time breakdown: hourly for "today", daily otherwise
        use_hourly = period == "today"
        bucket = defaultdict(lambda: {"tokens": 0, "cost": 0.0, "calls": 0})
        bucket_model = defaultdict(lambda: defaultdict(lambda: {"tokens": 0, "cost": 0.0}))
        all_models = set()

        for log in logs:
            if use_hourly:
                key = log["created_at"][11:13] + ":00"  # "HH:00"
            else:
                key = log["created_at"][:10]
            bucket[key]["tokens"] += log.get("total_tokens", 0)
            bucket[key]["cost"] += float(log.get("estimated_cost_usd", 0))
            bucket[key]["calls"] += 1
            model = log.get("model", "unknown")
            all_models.add(model)
            bucket_model[key][model]["tokens"] += log.get("total_tokens", 0)
            bucket_model[key][model]["cost"] += float(log.get("estimated_cost_usd", 0))

        # For today: ensure all 24 hours are present
        if use_hourly:
            for h in range(24):
                hk = f"{h:02d}:00"
                if hk not in bucket:
                    bucket[hk]  # triggers defaultdict

        sorted_keys = sorted(bucket.keys())
        daily_data = [{"date": key, **bucket[key]} for key in sorted_keys]

        # Build per-model series
        all_models_sorted = sorted(all_models)
        daily_by_model = {}
        for model in all_models_sorted:
            daily_by_model[model] = [
                {"date": key, "tokens": bucket_model[key][model]["tokens"], "cost": bucket_model[key][model]["cost"]}
                for key in sorted_keys
            ]

        # By model
        by_model = defaultdict(lambda: {"tokens": 0, "cost": 0.0, "calls": 0})
        for log in logs:
            model = log.get("model", "unknown")
            by_model[model]["tokens"] += log.get("total_tokens", 0)
            by_model[model]["cost"] += float(log.get("estimated_cost_usd", 0))
            by_model[model]["calls"] += 1

        model_data = [{"model": model, **data} for model, data in by_model.items()]

        # By user
        by_user = defaultdict(lambda: {"tokens": 0, "cost": 0.0, "calls": 0, "user_id": None})
        for log in logs:
            uid_str = log.get("user_id") or "unknown"
            by_user[uid_str]["tokens"] += log.get("total_tokens", 0)
            by_user[uid_str]["cost"] += float(log.get("estimated_cost_usd", 0))
            by_user[uid_str]["calls"] += 1
            by_user[uid_str]["user_id"] = uid_str

        user_data = list(by_user.values())

        # By operation
        by_operation = defaultdict(lambda: {"tokens": 0, "cost": 0.0, "calls": 0})
        for log in logs:
            op = log.get("operation", "unknown")
            by_operation[op]["tokens"] += log.get("total_tokens", 0)
            by_operation[op]["cost"] += float(log.get("estimated_cost_usd", 0))
            by_operation[op]["calls"] += 1

        operation_data = [{"operation": op, **data} for op, data in by_operation.items()]

        # ── Post statistics ─────────────────────────────────────
        try:
            posts = await db.get_post_stats(
                start_date=start_date,
                user_id=uid,
                company_id=cid
            )
        except Exception:
            posts = []

        total_created = len(posts)
        total_approved = sum(1 for p in posts if p.get("status") in ("approved", "scheduled", "published"))
        total_published = sum(1 for p in posts if p.get("status") == "published")

        # Daily post counts
        posts_daily = defaultdict(lambda: {"created": 0, "approved": 0, "published": 0})
        for p in posts:
            if use_hourly:
                created_key = p["created_at"][11:13] + ":00"
            else:
                created_key = p["created_at"][:10]
            posts_daily[created_key]["created"] += 1

            if p.get("status") in ("approved", "scheduled", "published"):
                ts = p.get("approved_at") or p["created_at"]
                if use_hourly:
                    ak = ts[11:13] + ":00"
                else:
                    ak = ts[:10]
                posts_daily[ak]["approved"] += 1

            if p.get("status") == "published":
                ts = p.get("published_at") or p["created_at"]
                if use_hourly:
                    pk = ts[11:13] + ":00"
                else:
                    pk = ts[:10]
                posts_daily[pk]["published"] += 1

        # For today: ensure all 24 hours present
        if use_hourly:
            for h in range(24):
                hk = f"{h:02d}:00"
                if hk not in posts_daily:
                    posts_daily[hk]

        sorted_post_keys = sorted(posts_daily.keys())
        posts_daily_data = [{"date": k, **posts_daily[k]} for k in sorted_post_keys]

        # ── Derived metrics ─────────────────────────────────────
        avg_cost_per_post = round(total_cost / total_created, 4) if total_created > 0 else 0
        avg_tokens_per_post = round(total_tokens / total_created) if total_created > 0 else 0

        # Monthly projection based on daily average
        num_days = len(daily_data) if daily_data else 1
        daily_avg_cost = total_cost / num_days if num_days > 0 else 0
        monthly_projection = round(daily_avg_cost * 30, 2)

        # Conversion rates
        approval_rate = round(total_approved / total_created * 100, 1) if total_created > 0 else 0
        publish_rate = round(total_published / total_created * 100, 1) if total_created > 0 else 0

        # Average time to approval / publication (in hours)
        approval_times = []
        publish_times = []
        for p in posts:
            created = p.get("created_at")
            if not created:
                continue
            try:
                created_dt = datetime.fromisoformat(created.replace("Z", "+00:00"))
            except Exception:
                continue

            if p.get("status") in ("approved", "scheduled", "published") and p.get("approved_at"):
                try:
                    approved_dt = datetime.fromisoformat(p["approved_at"].replace("Z", "+00:00"))
                    diff = (approved_dt - created_dt).total_seconds() / 3600
                    if diff >= 0:
                        approval_times.append(diff)
                except Exception:
                    pass

            if p.get("status") == "published" and p.get("published_at"):
                try:
                    published_dt = datetime.fromisoformat(p["published_at"].replace("Z", "+00:00"))
                    diff = (published_dt - created_dt).total_seconds() / 3600
                    if diff >= 0:
                        publish_times.append(diff)
                except Exception:
                    pass

        avg_hours_to_approval = round(sum(approval_times) / len(approval_times), 1) if approval_times else None
        avg_hours_to_publish = round(sum(publish_times) / len(publish_times), 1) if publish_times else None

        return {
            "total_tokens": total_tokens,
            "total_cost": round(total_cost, 4),
            "total_calls": total_calls,
            "daily": daily_data,
            "daily_by_model": daily_by_model,
            "by_model": model_data,
            "by_user": user_data,
            "by_operation": operation_data,
            "total_created": total_created,
            "total_approved": total_approved,
            "total_published": total_published,
            "posts_daily": posts_daily_data,
            "avg_cost_per_post": avg_cost_per_post,
            "avg_tokens_per_post": avg_tokens_per_post,
            "monthly_projection": monthly_projection,
            "approval_rate": approval_rate,
            "publish_rate": publish_rate,
            "avg_hours_to_approval": avg_hours_to_approval,
            "avg_hours_to_publish": avg_hours_to_publish,
            "period": period
        }
    except Exception as e:
        logger.error(f"Error getting statistics: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ==================== LICENSE KEYS MANAGEMENT ====================

@admin_router.get("/license-keys", response_class=HTMLResponse)
async def license_keys_page(request: Request):
    """License keys management page."""
    if not verify_auth(request):
        return RedirectResponse(url="/admin/login", status_code=302)

    try:
        keys = await db.list_license_keys()
        # Load offers for all keys in parallel
        offers_lists = await asyncio.gather(
            *[db.list_license_key_offers(k.id) for k in keys],
            return_exceptions=True
        )
        offers_by_key = {
            str(k.id): (v if not isinstance(v, Exception) else [])
            for k, v in zip(keys, offers_lists)
        }
        return templates.TemplateResponse("license_keys.html", {
            "request": request,
            "page": "license_keys",
            "keys": keys,
            "offers_by_key": offers_by_key,
            "total_keys": len(keys),
            "used_keys": len([k for k in keys if k.used]),
            "available_keys": len([k for k in keys if not k.used])
        })
    except Exception as e:
        logger.error(f"Error loading license keys: {e}")
        return templates.TemplateResponse("license_keys.html", {
            "request": request,
            "page": "license_keys",
            "error": str(e),
            "keys": [],
            "total_keys": 0,
            "used_keys": 0,
            "available_keys": 0
        })


@admin_router.post("/api/license-keys/generate")
async def generate_license_key(
    request: Request,
    max_employees: int = Form(...),
    daily_token_limit: Optional[int] = Form(None),
    description: str = Form(None)
):
    """Generate new license key."""
    if not verify_auth(request):
        raise HTTPException(status_code=401)

    try:
        # Generate random key (XXXX-XXXX-XXXX-XXXX format)
        key = "-".join([
            "".join(secrets.choice("ABCDEFGHJKLMNPQRSTUVWXYZ23456789") for _ in range(4))
            for _ in range(4)
        ])

        license_key = await db.create_license_key(
            key=key,
            max_employees=max_employees,
            daily_token_limit=daily_token_limit,
            description=description
        )

        return JSONResponse({"success": True, "key": license_key.model_dump(mode='json')})
    except Exception as e:
        logger.error(f"Error generating license key: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@admin_router.patch("/api/license-keys/{key_id}")
async def update_license_key_route(
    request: Request,
    key_id: str,
    max_employees: int = Form(...),
    daily_token_limit: Optional[int] = Form(None),
    description: str = Form(None)
):
    """Update license key limits."""
    if not verify_auth(request):
        raise HTTPException(status_code=401)

    try:
        updates = {
            "max_employees": max_employees,
            "daily_token_limit": daily_token_limit,
            "description": description
        }

        await db.update_license_key(UUID(key_id), updates)
        return JSONResponse({"success": True})
    except Exception as e:
        logger.error(f"Error updating license key: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@admin_router.delete("/api/license-keys/{key_id}")
async def delete_license_key_route(request: Request, key_id: str):
    """Delete license key."""
    if not verify_auth(request):
        raise HTTPException(status_code=401)

    try:
        await db.delete_license_key(UUID(key_id))
        return JSONResponse({"success": True})
    except Exception as e:
        logger.error(f"Error deleting license key: {e}")
        raise HTTPException(status_code=500, detail=str(e))


# ==================== LICENSE KEY OFFER (MOCO) ====================


@admin_router.get("/api/moco/companies")
async def search_moco_companies_endpoint(request: Request, term: str = ""):
    """Live-search MOCO companies by name."""
    if not verify_auth(request):
        raise HTTPException(status_code=401)

    try:
        companies = await moco_service.search_moco_companies(term)
        return JSONResponse(companies)
    except Exception as e:
        logger.error(f"MOCO company search error: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@admin_router.post("/api/license-keys/{key_id}/create-offer")
async def create_moco_offer_endpoint(
    request: Request,
    key_id: str,
    company_id: int = Form(...),
    company_name: str = Form(...),
    price: float = Form(...),
    payment_frequency: str = Form(...),
):
    """Create an offer in MOCO for the given license key."""
    if not verify_auth(request):
        raise HTTPException(status_code=401)

    try:
        key = await db.get_license_key_by_id(UUID(key_id))
        if not key:
            raise HTTPException(status_code=404, detail="Lizenzschlüssel nicht gefunden")

        plan_name = key.description or "Standard Plan"
        offer = await moco_service.create_moco_offer(
            company_id=company_id,
            company_name=company_name,
            price=price,
            payment_frequency=payment_frequency,
            plan_name=plan_name,
            max_employees=key.max_employees,
            daily_token_limit=key.daily_token_limit,
        )

        offer_url = offer.get("web_url") or offer.get("url") or f"https://{settings.moco_domain}.mocoapp.com/offers/{offer.get('id', '')}"

        # Persist offer in DB
        await db.create_license_key_offer(
            license_key_id=UUID(key_id),
            moco_offer_id=offer["id"],
            moco_offer_identifier=offer.get("identifier"),
            moco_offer_url=offer_url,
            offer_title=offer.get("title") or f"LinkedIn Content Automation – {plan_name}",
            company_name=company_name,
            price=price,
            payment_frequency=payment_frequency,
        )

        return JSONResponse({"success": True, "offer_url": offer_url})

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error creating MOCO offer: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@admin_router.post("/api/license-key-offers/{offer_id}/send")
async def send_license_key_offer(request: Request, offer_id: str):
    """Send a stored MOCO offer via email and mark it as sent."""
    if not verify_auth(request):
        raise HTTPException(status_code=401)

    try:
        from src.database.models import LicenseKeyOffer
        result = await asyncio.to_thread(
            lambda: db.client.table("license_key_offers")
            .select("*")
            .eq("id", offer_id)
            .execute()
        )
        if not result.data:
            raise HTTPException(status_code=404, detail="Angebot nicht gefunden")

        stored = LicenseKeyOffer(**result.data[0])
        await moco_service.send_moco_offer(stored.moco_offer_id)
        await db.update_license_key_offer_status(UUID(offer_id), "sent")

        return JSONResponse({"success": True})
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error sending offer {offer_id}: {e}")
        raise HTTPException(status_code=500, detail=str(e))


