"""Shared infrastructure for the Easy Ads backend.
Holds: env loading, db client, JWT helpers, object storage, Pydantic models,
language helpers and a couple of small constants used by multiple route modules.
"""
from dotenv import load_dotenv
from pathlib import Path
ROOT_DIR = Path(__file__).parent
load_dotenv(ROOT_DIR / '.env')

import os
import logging
import uuid
from datetime import datetime, timezone, timedelta
from typing import List, Optional

import bcrypt
import jwt
import requests
from fastapi import HTTPException, Request, Response
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel, Field, EmailStr

# ---------- Mongo ----------
mongo_url = os.environ['MONGO_URL']
client = AsyncIOMotorClient(mongo_url)
db = client[os.environ['DB_NAME']]

# ---------- Constants ----------
JWT_SECRET = os.environ['JWT_SECRET']
JWT_ALG = "HS256"
APP_NAME = os.environ.get("APP_NAME", "offislux")
EMERGENT_LLM_KEY = os.environ.get("EMERGENT_LLM_KEY")
STORAGE_URL = "https://integrations.emergentagent.com/objstore/api/v1/storage"
EXTRA_ADJUSTMENT_PRICE = 5.00
EXTRA_ADJUSTMENT_CURRENCY = "eur"

logger = logging.getLogger("easyads")

# ---------- Auth helpers ----------
def hash_password(p: str) -> str:
    return bcrypt.hashpw(p.encode(), bcrypt.gensalt()).decode()


def verify_password(p: str, h: str) -> bool:
    try:
        return bcrypt.checkpw(p.encode(), h.encode())
    except Exception:
        return False


def create_token(user_id: str, email: str, kind: str = "access") -> str:
    delta = timedelta(minutes=15) if kind == "access" else timedelta(days=7)
    payload = {"sub": user_id, "email": email, "type": kind, "exp": datetime.now(timezone.utc) + delta}
    return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALG)


def set_auth_cookies(resp: Response, user_id: str, email: str):
    access = create_token(user_id, email, "access")
    refresh = create_token(user_id, email, "refresh")
    resp.set_cookie("access_token", access, httponly=True, secure=True, samesite="none", max_age=900, path="/")
    resp.set_cookie("refresh_token", refresh, httponly=True, secure=True, samesite="none", max_age=604800, path="/")
    return access


async def get_current_user(request: Request) -> dict:
    token = request.cookies.get("access_token")
    if not token:
        auth = request.headers.get("Authorization", "")
        if auth.startswith("Bearer "):
            token = auth[7:]
    if not token:
        raise HTTPException(401, "Not authenticated")
    try:
        payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALG])
        if payload.get("type") != "access":
            raise HTTPException(401, "Invalid token type")
        user = await db.users.find_one({"id": payload["sub"]}, {"_id": 0, "password_hash": 0})
        if not user:
            raise HTTPException(401, "User not found")
        return user
    except jwt.ExpiredSignatureError:
        raise HTTPException(401, "Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(401, "Invalid token")


# ---------- Object storage ----------
_storage_key: Optional[str] = None


def init_storage() -> Optional[str]:
    global _storage_key
    if _storage_key:
        return _storage_key
    if not EMERGENT_LLM_KEY:
        logger.warning("EMERGENT_LLM_KEY not set; uploads will fail")
        return None
    try:
        r = requests.post(f"{STORAGE_URL}/init", json={"emergent_key": EMERGENT_LLM_KEY}, timeout=30)
        r.raise_for_status()
        _storage_key = r.json()["storage_key"]
        return _storage_key
    except Exception as e:
        logger.error(f"Storage init failed: {e}")
        return None


def put_object(path: str, data: bytes, content_type: str) -> dict:
    key = init_storage()
    if not key:
        raise HTTPException(503, "Storage not available")
    r = requests.put(f"{STORAGE_URL}/objects/{path}",
                     headers={"X-Storage-Key": key, "Content-Type": content_type},
                     data=data, timeout=120)
    r.raise_for_status()
    return r.json()


def get_object(path: str):
    key = init_storage()
    if not key:
        raise HTTPException(503, "Storage not available")
    r = requests.get(f"{STORAGE_URL}/objects/{path}",
                     headers={"X-Storage-Key": key}, timeout=60)
    r.raise_for_status()
    return r.content, r.headers.get("Content-Type", "application/octet-stream")


# ---------- Language helpers ----------
LANG_NAMES = {
    "en": "English",
    "pt": "Portuguese (European)",
    "pt-BR": "Brazilian Portuguese",
    "it": "Italian",
    "es": "Spanish",
    "fr": "French",
    "de": "German",
}


def _lang_for_country(country: str) -> str:
    c = (country or "").strip().lower()
    if c in ("portugal", "portuguese"): return "pt"
    if c in ("brazil", "brasil"): return "pt"
    if c in ("italy", "italia"): return "it"
    if c in ("spain", "españa", "espana"): return "es"
    if c in ("france", "francais"): return "fr"
    if c in ("germany", "deutschland"): return "de"
    return "en"


# ---------- Pydantic models ----------
class RegisterIn(BaseModel):
    email: EmailStr
    password: str
    name: Optional[str] = None


class LoginIn(BaseModel):
    email: EmailStr
    password: str


class BusinessProfileIn(BaseModel):
    business_name: str
    nif: str = Field(..., description="Tax ID / VAT")
    address: str
    city: str
    country: str = "Portugal"
    iban: Optional[str] = None
    facebook_page: Optional[str] = None
    instagram_handle: Optional[str] = None
    phone: Optional[str] = None


class CampaignCreateIn(BaseModel):
    niche_id: str
    management_plan_id: str
    media_ids: List[str] = []
    headline: Optional[str] = None
    description: Optional[str] = None
    geofence_km: int = 40
    format: str = "9x16"
    gender: str = "all"
    age_min: int = 18
    age_max: int = 65
    interests: List[str] = []
    platforms: List[str] = ["facebook", "instagram", "threads"]
    language: str = "en"
    accepted_terms: bool = False


class CampaignPatchIn(BaseModel):
    headline: Optional[str] = None
    description: Optional[str] = None
    geofence_km: Optional[int] = None
    format: Optional[str] = None
    gender: Optional[str] = None
    age_min: Optional[int] = None
    age_max: Optional[int] = None
    interests: Optional[List[str]] = None
    platforms: Optional[List[str]] = None
    language: Optional[str] = None
    media_ids: Optional[List[str]] = None
    adjustment_type: str = "copy"


class AdjustmentIn(BaseModel):
    campaign_id: str
    type: str
    note: str


class CheckoutIn(BaseModel):
    item_type: str
    item_id: str
    origin_url: str
    campaign_id: Optional[str] = None


class ForecastIn(BaseModel):
    management_plan_id: str
    geofence_km: int = 40
    gender: str = "all"
    interests: List[str] = []
    platforms: List[str] = ["facebook", "instagram", "threads"]


class RenderVideoIn(BaseModel):
    music_id: Optional[str] = None
    vibe: Optional[str] = None  # 'professional' | 'energetic' | 'minimal' | 'luxury'
    aspect_ratio: Optional[str] = None  # '9:16' (default) | '1:1'
    duration_seconds: Optional[float] = None
    brand_text: Optional[str] = None
    brand_logo_media_id: Optional[str] = None
    user_video_media_id: Optional[str] = None
    custom_lat: Optional[float] = None
    custom_lng: Optional[float] = None


class CopyGenIn(BaseModel):
    niche_id: str
    business_name: str
    city: Optional[str] = None
    language: Optional[str] = None
