from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from backend.app.core.auth import RequirePermissionIfAuthEnabled
from backend.app.core.database import get_db
from backend.app.core.permissions import Permission
from backend.app.models.filament import Filament
from backend.app.models.user import User
from backend.app.schemas.filament import (
    FilamentCostCalculation,
    FilamentCreate,
    FilamentResponse,
    FilamentUpdate,
)

router = APIRouter(prefix="/filament-catalog", tags=["filament-catalog"])


@router.get("/", response_model=list[FilamentResponse])
async def list_filaments(
    db: AsyncSession = Depends(get_db),
    _: User | None = RequirePermissionIfAuthEnabled(Permission.FILAMENTS_READ),
):
    """List all filaments."""
    result = await db.execute(select(Filament).order_by(Filament.type, Filament.name))
    return list(result.scalars().all())


@router.post("/", response_model=FilamentResponse)
async def create_filament(
    filament_data: FilamentCreate,
    db: AsyncSession = Depends(get_db),
    _: User | None = RequirePermissionIfAuthEnabled(Permission.FILAMENTS_CREATE),
):
    """Create a new filament entry."""
    filament = Filament(**filament_data.model_dump())
    db.add(filament)
    await db.commit()
    await db.refresh(filament)
    return filament


@router.get("/{filament_id}", response_model=FilamentResponse)
async def get_filament(
    filament_id: int,
    db: AsyncSession = Depends(get_db),
    _: User | None = RequirePermissionIfAuthEnabled(Permission.FILAMENTS_READ),
):
    """Get a specific filament."""
    result = await db.execute(select(Filament).where(Filament.id == filament_id))
    filament = result.scalar_one_or_none()
    if not filament:
        raise HTTPException(404, "Filament not found")
    return filament


@router.patch("/{filament_id}", response_model=FilamentResponse)
async def update_filament(
    filament_id: int,
    filament_data: FilamentUpdate,
    db: AsyncSession = Depends(get_db),
    _: User | None = RequirePermissionIfAuthEnabled(Permission.FILAMENTS_UPDATE),
):
    """Update a filament."""
    result = await db.execute(select(Filament).where(Filament.id == filament_id))
    filament = result.scalar_one_or_none()
    if not filament:
        raise HTTPException(404, "Filament not found")

    for field, value in filament_data.model_dump(exclude_unset=True).items():
        setattr(filament, field, value)

    await db.commit()
    await db.refresh(filament)
    return filament


@router.delete("/{filament_id}")
async def delete_filament(
    filament_id: int,
    db: AsyncSession = Depends(get_db),
    _: User | None = RequirePermissionIfAuthEnabled(Permission.FILAMENTS_DELETE),
):
    """Delete a filament."""
    result = await db.execute(select(Filament).where(Filament.id == filament_id))
    filament = result.scalar_one_or_none()
    if not filament:
        raise HTTPException(404, "Filament not found")

    await db.delete(filament)
    await db.commit()
    return {"status": "deleted"}


@router.post("/calculate-cost", response_model=FilamentCostCalculation)
async def calculate_cost(
    filament_id: int,
    weight_grams: float,
    db: AsyncSession = Depends(get_db),
    _: User | None = RequirePermissionIfAuthEnabled(Permission.FILAMENTS_READ),
):
    """Calculate the cost for a given weight of filament."""
    result = await db.execute(select(Filament).where(Filament.id == filament_id))
    filament = result.scalar_one_or_none()
    if not filament:
        raise HTTPException(404, "Filament not found")

    cost = (weight_grams / 1000) * filament.cost_per_kg

    return FilamentCostCalculation(
        filament_id=filament.id,
        filament_name=filament.name,
        weight_grams=weight_grams,
        cost=round(cost, 2),
        currency=filament.currency,
    )


@router.get("/by-type/{filament_type}", response_model=list[FilamentResponse])
async def get_filaments_by_type(
    filament_type: str,
    db: AsyncSession = Depends(get_db),
    _: User | None = RequirePermissionIfAuthEnabled(Permission.FILAMENTS_READ),
):
    """Get all filaments of a specific type."""
    result = await db.execute(select(Filament).where(Filament.type.ilike(f"%{filament_type}%")).order_by(Filament.name))
    return list(result.scalars().all())


@router.post("/seed-defaults")
async def seed_default_filaments(
    db: AsyncSession = Depends(get_db),
    _: User | None = RequirePermissionIfAuthEnabled(Permission.FILAMENTS_CREATE),
):
    """Seed the database with common filament types."""
    defaults = [
        {
            "name": "Generic PLA",
            "type": "PLA",
            "cost_per_kg": 20.0,
            "print_temp_min": 190,
            "print_temp_max": 220,
            "bed_temp_min": 50,
            "bed_temp_max": 60,
            "density": 1.24,
        },
        {
            "name": "Generic PETG",
            "type": "PETG",
            "cost_per_kg": 25.0,
            "print_temp_min": 230,
            "print_temp_max": 250,
            "bed_temp_min": 70,
            "bed_temp_max": 80,
            "density": 1.27,
        },
        {
            "name": "Generic ABS",
            "type": "ABS",
            "cost_per_kg": 22.0,
            "print_temp_min": 230,
            "print_temp_max": 260,
            "bed_temp_min": 90,
            "bed_temp_max": 110,
            "density": 1.04,
        },
        {
            "name": "Generic TPU",
            "type": "TPU",
            "cost_per_kg": 35.0,
            "print_temp_min": 220,
            "print_temp_max": 250,
            "bed_temp_min": 40,
            "bed_temp_max": 60,
            "density": 1.21,
        },
        {
            "name": "Generic ASA",
            "type": "ASA",
            "cost_per_kg": 28.0,
            "print_temp_min": 240,
            "print_temp_max": 260,
            "bed_temp_min": 90,
            "bed_temp_max": 110,
            "density": 1.07,
        },
        {
            "name": "Bambu PLA Basic",
            "type": "PLA",
            "brand": "Bambu Lab",
            "cost_per_kg": 20.0,
            "print_temp_min": 190,
            "print_temp_max": 220,
            "bed_temp_min": 35,
            "bed_temp_max": 55,
            "density": 1.24,
        },
        {
            "name": "Bambu PLA Matte",
            "type": "PLA",
            "brand": "Bambu Lab",
            "cost_per_kg": 25.0,
            "print_temp_min": 190,
            "print_temp_max": 220,
            "bed_temp_min": 35,
            "bed_temp_max": 55,
            "density": 1.24,
        },
        {
            "name": "Bambu PETG Basic",
            "type": "PETG",
            "brand": "Bambu Lab",
            "cost_per_kg": 25.0,
            "print_temp_min": 250,
            "print_temp_max": 270,
            "bed_temp_min": 70,
            "bed_temp_max": 80,
            "density": 1.27,
        },
        {
            "name": "Bambu ABS",
            "type": "ABS",
            "brand": "Bambu Lab",
            "cost_per_kg": 30.0,
            "print_temp_min": 260,
            "print_temp_max": 280,
            "bed_temp_min": 90,
            "bed_temp_max": 100,
            "density": 1.04,
        },
    ]

    created = 0
    for filament_data in defaults:
        # Check if already exists
        result = await db.execute(
            select(Filament).where(
                Filament.name == filament_data["name"],
                Filament.type == filament_data["type"],
            )
        )
        if result.scalar_one_or_none():
            continue

        filament = Filament(**filament_data)
        db.add(filament)
        created += 1

    await db.commit()
    return {"created": created, "message": f"Created {created} default filaments"}
