"""
Backup management API endpoints.
"""
import os
import tempfile
import shutil
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks, UploadFile, File
from fastapi.responses import FileResponse
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from pydantic import BaseModel
from typing import Optional

from database import get_db
from models.user import User
from models.settings import KitchenSettings
from models.backup import BackupHistory
from auth import get_current_user, require_cap
from services.backup_service import BackupService

router = APIRouter()


# ============ Pydantic Models ============

class BackupSettingsResponse(BaseModel):
    backup_frequency: str | None
    backup_retention_count: int
    backup_destination: str | None
    backup_time: str | None
    backup_nextcloud_path: str | None
    backup_smb_host: str | None
    backup_smb_share: str | None
    backup_smb_username: str | None
    backup_smb_password_set: bool
    backup_smb_path: str | None
    backup_last_run_at: str | None
    backup_last_status: str | None
    backup_last_error: str | None

    class Config:
        from_attributes = True


class BackupSettingsUpdate(BaseModel):
    backup_frequency: str | None = None  # "daily", "weekly", "manual"
    backup_retention_count: int | None = None
    backup_destination: str | None = None  # "local", "nextcloud", "smb"
    backup_time: str | None = None  # "HH:MM"
    backup_nextcloud_path: str | None = None
    backup_smb_host: str | None = None
    backup_smb_share: str | None = None
    backup_smb_username: str | None = None
    backup_smb_password: str | None = None
    backup_smb_path: str | None = None


class BackupHistoryResponse(BaseModel):
    id: int
    backup_type: str
    destination: str
    status: str
    filename: str
    file_size_bytes: int | None
    invoice_count: int | None
    file_count: int | None
    started_at: str
    completed_at: str | None
    error_message: str | None
    triggered_by_username: str | None

    class Config:
        from_attributes = True


class BackupCreateResponse(BaseModel):
    message: str
    status: str
    backup_id: int | None = None


class BackupRestoreResponse(BaseModel):
    status: str
    message: str


# ============ Settings Endpoints ============

@router.get("/settings", response_model=BackupSettingsResponse)
async def get_backup_settings(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get backup settings"""
    result = await db.execute(
        select(KitchenSettings).where(KitchenSettings.kitchen_id == current_user.kitchen_id)
    )
    settings = result.scalar_one_or_none()

    if not settings:
        return BackupSettingsResponse(
            backup_frequency="manual",
            backup_retention_count=7,
            backup_destination="local",
            backup_time="03:00",
            backup_nextcloud_path="/Backups",
            backup_smb_host=None,
            backup_smb_share=None,
            backup_smb_username=None,
            backup_smb_password_set=False,
            backup_smb_path="/backups",
            backup_last_run_at=None,
            backup_last_status=None,
            backup_last_error=None
        )

    return BackupSettingsResponse(
        backup_frequency=settings.backup_frequency,
        backup_retention_count=settings.backup_retention_count,
        backup_destination=settings.backup_destination,
        backup_time=settings.backup_time,
        backup_nextcloud_path=settings.backup_nextcloud_path,
        backup_smb_host=settings.backup_smb_host,
        backup_smb_share=settings.backup_smb_share,
        backup_smb_username=settings.backup_smb_username,
        backup_smb_password_set=bool(settings.backup_smb_password),
        backup_smb_path=settings.backup_smb_path,
        backup_last_run_at=settings.backup_last_run_at.isoformat() if settings.backup_last_run_at else None,
        backup_last_status=settings.backup_last_status,
        backup_last_error=settings.backup_last_error
    )


@router.patch("/settings", response_model=BackupSettingsResponse)
async def update_backup_settings(
    update: BackupSettingsUpdate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Update backup settings"""
    if not current_user.is_admin:
        raise HTTPException(status_code=403, detail="Admin only")

    result = await db.execute(
        select(KitchenSettings).where(KitchenSettings.kitchen_id == current_user.kitchen_id)
    )
    settings = result.scalar_one_or_none()

    if not settings:
        settings = KitchenSettings(kitchen_id=current_user.kitchen_id)
        db.add(settings)

    update_data = update.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        if field == 'backup_smb_password' and value:
            setattr(settings, field, value)
        elif value is not None:
            setattr(settings, field, value)

    await db.commit()
    await db.refresh(settings)

    return BackupSettingsResponse(
        backup_frequency=settings.backup_frequency,
        backup_retention_count=settings.backup_retention_count,
        backup_destination=settings.backup_destination,
        backup_time=settings.backup_time,
        backup_nextcloud_path=settings.backup_nextcloud_path,
        backup_smb_host=settings.backup_smb_host,
        backup_smb_share=settings.backup_smb_share,
        backup_smb_username=settings.backup_smb_username,
        backup_smb_password_set=bool(settings.backup_smb_password),
        backup_smb_path=settings.backup_smb_path,
        backup_last_run_at=settings.backup_last_run_at.isoformat() if settings.backup_last_run_at else None,
        backup_last_status=settings.backup_last_status,
        backup_last_error=settings.backup_last_error
    )


# ============ Backup Operations ============

async def _run_backup_task(db: AsyncSession, kitchen_id: int, user_id: int):
    """Background task to run backup"""
    backup_service = BackupService(db, kitchen_id)
    await backup_service.create_backup(user_id=user_id, backup_type="manual")


@router.post("/create", response_model=BackupCreateResponse)
async def create_backup(
    background_tasks: BackgroundTasks,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Trigger a manual backup"""
    if not current_user.is_admin:
        raise HTTPException(status_code=403, detail="Admin only")

    # Run backup synchronously for now to get immediate feedback
    backup_service = BackupService(db, current_user.kitchen_id)
    success, message, backup = await backup_service.create_backup(
        user_id=current_user.id,
        backup_type="manual"
    )

    if not success:
        raise HTTPException(status_code=500, detail=message)

    return BackupCreateResponse(
        message=message,
        status="success",
        backup_id=backup.id if backup else None
    )


@router.get("/history", response_model=list[BackupHistoryResponse])
async def list_backups(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """List backup history"""
    backup_service = BackupService(db, current_user.kitchen_id)
    backups = await backup_service.list_backups()

    return [
        BackupHistoryResponse(
            id=b.id,
            backup_type=b.backup_type,
            destination=b.destination,
            status=b.status,
            filename=b.filename,
            file_size_bytes=b.file_size_bytes,
            invoice_count=b.invoice_count,
            file_count=b.file_count,
            started_at=b.started_at.isoformat(),
            completed_at=b.completed_at.isoformat() if b.completed_at else None,
            error_message=b.error_message,
            triggered_by_username=b.triggered_by_user.name if b.triggered_by_user else None
        )
        for b in backups
    ]


@router.post("/{backup_id}/restore", response_model=BackupRestoreResponse)
async def restore_backup(
    backup_id: int,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Restore from a backup"""
    if not current_user.is_admin:
        raise HTTPException(status_code=403, detail="Admin only")

    backup_service = BackupService(db, current_user.kitchen_id)
    success, message = await backup_service.restore_backup(backup_id)

    if not success:
        raise HTTPException(status_code=400, detail=message)

    return BackupRestoreResponse(status="success", message=message)


@router.delete("/{backup_id}")
async def delete_backup(
    backup_id: int,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Delete a backup"""
    if not current_user.is_admin:
        raise HTTPException(status_code=403, detail="Admin only")

    backup_service = BackupService(db, current_user.kitchen_id)
    success, message = await backup_service.delete_backup(backup_id)

    if not success:
        raise HTTPException(status_code=400, detail=message)

    return {"message": message}


@router.get("/{backup_id}/download")
async def download_backup(
    backup_id: int,
    token: str,
    db: AsyncSession = Depends(get_db)
):
    """Download a backup file. Auth via token query param for direct browser downloads."""
    from auth import get_current_user, require_cap_from_token

    current_user = await get_current_user_from_token(token, db)
    if not current_user:
        raise HTTPException(status_code=401, detail="Invalid token")
    if not current_user.is_admin:
        raise HTTPException(status_code=403, detail="Admin only")

    backup_service = BackupService(db, current_user.kitchen_id)
    backup = await backup_service.get_backup(backup_id)

    if not backup:
        raise HTTPException(status_code=404, detail="Backup not found")

    if backup.status != "success":
        raise HTTPException(status_code=400, detail="Cannot download failed backup")

    # Handle local backups
    if not backup.file_path.startswith("nextcloud:"):
        if not os.path.exists(backup.file_path):
            raise HTTPException(status_code=404, detail="Backup file not found on disk")

        return FileResponse(
            path=backup.file_path,
            filename=backup.filename,
            media_type="application/zip"
        )

    # Handle Nextcloud backups - download to temp file
    from services.nextcloud_service import NextcloudService

    result = await db.execute(
        select(KitchenSettings).where(KitchenSettings.kitchen_id == current_user.kitchen_id)
    )
    settings = result.scalar_one_or_none()

    if not settings or not settings.nextcloud_host:
        raise HTTPException(status_code=400, detail="Nextcloud not configured")

    nc_path = backup.file_path.replace("nextcloud:", "")
    nc = NextcloudService(
        settings.nextcloud_host,
        settings.nextcloud_username,
        settings.nextcloud_password,
        ""
    )
    success, content = await nc.download_file(nc_path)
    await nc.close()

    if not success:
        raise HTTPException(status_code=500, detail=f"Failed to download from Nextcloud: {content}")

    # Write to temp file and return
    temp_dir = tempfile.mkdtemp()
    temp_path = os.path.join(temp_dir, backup.filename)
    with open(temp_path, 'wb') as f:
        f.write(content)

    return FileResponse(
        path=temp_path,
        filename=backup.filename,
        media_type="application/zip",
        background=lambda: shutil.rmtree(temp_dir, ignore_errors=True)
    )


@router.post("/upload", response_model=BackupRestoreResponse)
async def upload_and_restore_backup(
    file: UploadFile = File(...),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Upload a backup file and restore from it"""
    if not current_user.is_admin:
        raise HTTPException(status_code=403, detail="Admin only")

    if not file.filename.endswith('.zip'):
        raise HTTPException(status_code=400, detail="Only ZIP files are accepted")

    backup_service = BackupService(db, current_user.kitchen_id)
    success, message = await backup_service.restore_from_upload(file)

    if not success:
        raise HTTPException(status_code=400, detail=message)

    return BackupRestoreResponse(status="success", message=message)
