from __future__ import annotations

from fastapi import APIRouter, File, Form, HTTPException, UploadFile
from pydantic import BaseModel, EmailStr, Field

from app.config import settings
from app.services.otp_mail import otp_service, send_otp_email
from app.services import avatars as avatar_service
from app.services import users as user_service

router = APIRouter(prefix="/auth", tags=["auth"])

DEMO_EMAIL = "admin@gmail.com"
DEMO_PASSWORD = "Thetitan@1234"
DEMO_NAME = "Sara"


class SendOtpRequest(BaseModel):
    email: EmailStr
    platform: str = Field(pattern="^(firm|client)$")


class SendOtpResponse(BaseModel):
    ok: bool = True
    message: str
    emailed: bool
    demo_otp: str | None = None


class VerifyOtpRequest(BaseModel):
    email: EmailStr
    platform: str = Field(pattern="^(firm|client)$")
    otp: str = Field(min_length=4, max_length=8)


class VerifyOtpResponse(BaseModel):
    ok: bool = True
    reset_token: str


class ConfirmResetRequest(BaseModel):
    email: EmailStr
    platform: str = Field(pattern="^(firm|client)$")
    reset_token: str
    new_password: str = Field(min_length=8, max_length=128)


class ConfirmResetResponse(BaseModel):
    ok: bool = True
    message: str


class SignupRequest(BaseModel):
    platform: str = Field(pattern="^(firm|client)$")
    name: str = Field(min_length=1, max_length=255)
    email: EmailStr
    password: str = Field(min_length=8, max_length=128)
    company: str | None = None


class LoginRequest(BaseModel):
    platform: str = Field(pattern="^(firm|client)$")
    email: EmailStr
    password: str


class ProfileUpdateRequest(BaseModel):
    platform: str = Field(pattern="^(firm|client)$")
    email: EmailStr
    name: str = Field(min_length=1, max_length=255)
    company: str | None = None


class ChangePasswordRequest(BaseModel):
    platform: str = Field(pattern="^(firm|client)$")
    email: EmailStr
    current_password: str
    new_password: str = Field(min_length=8, max_length=128)


class AvatarRemoveRequest(BaseModel):
    platform: str = Field(pattern="^(firm|client)$")
    email: EmailStr


class UserOut(BaseModel):
    email: str
    name: str
    company: str | None = None
    platform: str
    avatar_url: str | None = None
    client_id: str | None = None


def _user_out(row) -> UserOut:
    return UserOut(
        email=row.email,
        name=row.name,
        company=row.company,
        platform=row.platform,
        avatar_url=getattr(row, "avatar_url", None),
        client_id=getattr(row, "client_id", None),
    )


def _demo_out(platform: str, *, name: str | None = None, company: str | None = None) -> UserOut:
    row = user_service.get_user(platform, DEMO_EMAIL)
    if platform == "client":
        row = user_service.provision_client_account("client", DEMO_EMAIL) or row
    return UserOut(
        email=DEMO_EMAIL,
        name=(name or (row.name if row else DEMO_NAME)),
        company=company if company is not None else (row.company if row else None),
        platform=platform,
        avatar_url=row.avatar_url if row else None,
        client_id=row.client_id if row else None,
    )


@router.post("/signup", response_model=UserOut)
def signup(payload: SignupRequest) -> UserOut:
    email = str(payload.email).lower()
    if email == DEMO_EMAIL:
        raise HTTPException(status_code=400, detail="This email is already registered")
    try:
        row = user_service.create_user(
            platform=payload.platform,
            email=email,
            password=payload.password,
            name=payload.name,
            company=payload.company,
        )
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc
    if payload.platform == "client":
        row = user_service.provision_client_account("client", email) or row
    return _user_out(row)


@router.post("/login", response_model=UserOut)
def login(payload: LoginRequest) -> UserOut:
    email = str(payload.email).lower()
    if email == DEMO_EMAIL and payload.password == DEMO_PASSWORD:
        # Ensure demo row exists so avatar/profile can persist
        user_service.ensure_user(
            platform=payload.platform,
            email=DEMO_EMAIL,
            name=DEMO_NAME,
        )
        return _demo_out(payload.platform)

    row = user_service.authenticate_user(payload.platform, email, payload.password)
    if not row:
        raise HTTPException(status_code=401, detail="Invalid email or password")
    if payload.platform == "client":
        row = user_service.provision_client_account("client", email) or row
    return _user_out(row)


@router.patch("/profile", response_model=UserOut)
def update_profile(payload: ProfileUpdateRequest) -> UserOut:
    email = str(payload.email).lower()
    if email == DEMO_EMAIL:
        row = user_service.ensure_user(
            platform=payload.platform,
            email=DEMO_EMAIL,
            name=payload.name.strip() or DEMO_NAME,
            company=payload.company,
        )
        try:
            row = user_service.update_user_profile(
                payload.platform,
                email,
                name=payload.name,
                company=payload.company,
            )
        except ValueError:
            pass
        return _user_out(row) if row else _demo_out(
            payload.platform,
            name=payload.name.strip(),
            company=(payload.company or "").strip() or None,
        )
    try:
        row = user_service.update_user_profile(
            payload.platform,
            email,
            name=payload.name,
            company=payload.company,
        )
    except ValueError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc
    return _user_out(row)


@router.post("/avatar", response_model=UserOut)
async def upload_avatar(
    platform: str = Form(...),
    email: EmailStr = Form(...),
    file: UploadFile = File(...),
) -> UserOut:
    """Upload/replace the authenticated user's own profile photo."""
    if platform not in ("firm", "client"):
        raise HTTPException(status_code=400, detail="Invalid platform")
    email_n = str(email).lower()
    # Only the account owner can upload (identity must match form fields)
    if email_n == DEMO_EMAIL:
        user_service.ensure_user(platform=platform, email=DEMO_EMAIL, name=DEMO_NAME)
    else:
        if not user_service.get_user(platform, email_n):
            raise HTTPException(status_code=404, detail="Account not found")

    existing = user_service.get_user(platform, email_n)
    old_url = existing.avatar_url if existing else None

    try:
        new_url = await avatar_service.save_avatar_file(file, platform=platform, email=email_n)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail=str(exc)) from exc

    try:
        row = user_service.set_avatar_url(platform, email_n, new_url)
    except ValueError as exc:
        avatar_service.delete_avatar_file(new_url)
        raise HTTPException(status_code=404, detail=str(exc)) from exc

    if old_url and old_url != new_url:
        avatar_service.delete_avatar_file(old_url)
    return _user_out(row)


@router.delete("/avatar", response_model=UserOut)
def remove_avatar(payload: AvatarRemoveRequest) -> UserOut:
    email_n = str(payload.email).lower()
    if email_n == DEMO_EMAIL:
        user_service.ensure_user(platform=payload.platform, email=DEMO_EMAIL, name=DEMO_NAME)

    row = user_service.get_user(payload.platform, email_n)
    if not row:
        raise HTTPException(status_code=404, detail="Account not found")

    old_url = row.avatar_url
    try:
        updated = user_service.set_avatar_url(payload.platform, email_n, None)
    except ValueError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc

    avatar_service.delete_avatar_file(old_url)
    return _user_out(updated)


@router.post("/change-password")
def change_password(payload: ChangePasswordRequest) -> dict[str, bool | str]:
    email = str(payload.email).lower()
    if email == DEMO_EMAIL:
        raise HTTPException(status_code=400, detail="The demo account password cannot be changed here")

    row = user_service.authenticate_user(payload.platform, email, payload.current_password)
    if not row:
        raise HTTPException(status_code=400, detail="Current password is incorrect")
    user_service.update_user_password(payload.platform, email, payload.new_password)
    return {"ok": True, "message": "Password updated"}


@router.post("/forgot-password/send-otp", response_model=SendOtpResponse)
def send_otp(payload: SendOtpRequest) -> SendOtpResponse:
    email = str(payload.email).lower()
    if email == DEMO_EMAIL:
        raise HTTPException(
            status_code=400,
            detail="The demo account password cannot be reset. Use the demo sign-in credentials.",
        )
    if not user_service.get_user(payload.platform, email):
        raise HTTPException(status_code=404, detail="No account found with this email")

    otp = otp_service.create_otp(payload.platform, email)
    emailed = send_otp_email(email, otp, payload.platform)
    include_demo = not emailed or settings.otp_expose_demo
    return SendOtpResponse(
        message="OTP sent to your email" if emailed else "OTP generated (email not configured — use the demo code)",
        emailed=emailed,
        demo_otp=otp if include_demo else None,
    )


@router.post("/forgot-password/verify-otp", response_model=VerifyOtpResponse)
def verify_otp(payload: VerifyOtpRequest) -> VerifyOtpResponse:
    token = otp_service.verify_otp(payload.platform, str(payload.email), payload.otp)
    if not token:
        raise HTTPException(status_code=400, detail="Invalid or expired OTP")
    return VerifyOtpResponse(reset_token=token)


@router.post("/forgot-password/confirm", response_model=ConfirmResetResponse)
def confirm_reset(payload: ConfirmResetRequest) -> ConfirmResetResponse:
    email = str(payload.email).lower()
    ok = otp_service.consume_reset_token(payload.reset_token, payload.platform, email)
    if not ok:
        raise HTTPException(status_code=400, detail="Invalid or expired reset token")
    try:
        user_service.update_user_password(payload.platform, email, payload.new_password)
    except ValueError as exc:
        raise HTTPException(status_code=404, detail=str(exc)) from exc
    return ConfirmResetResponse(message="Password updated")
