"""Device Pairing API — IBO Player Pro style."""
import secrets
from datetime import datetime, timezone
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.orm import Session

from db.models import Device
from db.session import get_session

router = APIRouter(prefix="/api/device", tags=["device-pairing"])


class DeviceRegisterResponse(BaseModel):
    device_id: str
    mac_address: str
    pin: str
    created_at: str


class DeviceStatusResponse(BaseModel):
    device_id: str
    mac_address: str
    paired: bool
    profile_name: Optional[str] = None


class PairRequest(BaseModel):
    pin: str
    server_url: str
    username: str
    password: str
    profile_name: str = "Paired Device"


class PairResponse(BaseModel):
    success: bool
    device_id: str
    message: str


class DeviceConfigResponse(BaseModel):
    device_id: str
    mac_address: str
    server_url: str
    username: str
    password: str
    profile_name: str
    paired_at: str


def generate_pseudo_mac() -> str:
    octets = [secrets.randbelow(256) for _ in range(6)]
    octets[0] = (octets[0] | 0x02) & 0xFE
    return ":".join(f"{b:02X}" for b in octets)


def generate_pin() -> str:
    return f"{secrets.randbelow(1000000):06d}"


def generate_device_id() -> str:
    return secrets.token_hex(8).upper()


@router.post("/register", response_model=DeviceRegisterResponse)
def register_device(db: Session = Depends(get_session)):
    device_id = generate_device_id()
    mac_address = generate_pseudo_mac()
    # Ensure PIN is unique
    for _ in range(20):
        pin = generate_pin()
        if not db.scalar(select(Device).where(Device.pin == pin)):
            break
    else:
        raise HTTPException(status_code=500, detail="Could not allocate unique PIN")

    now = datetime.now(timezone.utc).isoformat()
    d = Device(
        device_id=device_id,
        mac_address=mac_address,
        pin=pin,
        paired=False,
        server_url=None,
        username=None,
        password=None,
        profile_name=None,
        created_at=now,
        paired_at=None,
    )
    db.add(d)
    db.commit()
    return DeviceRegisterResponse(device_id=device_id, mac_address=mac_address, pin=pin, created_at=now)


@router.get("/status/{device_id}", response_model=DeviceStatusResponse)
def device_status(device_id: str, db: Session = Depends(get_session)):
    d = db.get(Device, device_id)
    if not d:
        raise HTTPException(status_code=404, detail="Device not found")
    return DeviceStatusResponse(
        device_id=d.device_id,
        mac_address=d.mac_address,
        paired=bool(d.paired),
        profile_name=d.profile_name,
    )


@router.post("/pair", response_model=PairResponse)
def pair_device(req: PairRequest, db: Session = Depends(get_session)):
    pin = req.pin.strip()
    if len(pin) != 6 or not pin.isdigit():
        raise HTTPException(status_code=400, detail="PIN must be 6 digits")

    d = db.scalar(select(Device).where(Device.pin == pin, Device.paired == False))  # noqa: E712
    if not d:
        raise HTTPException(status_code=404, detail="Invalid PIN or device already paired")

    d.paired = True
    d.server_url = req.server_url
    d.username = req.username
    d.password = req.password
    d.profile_name = req.profile_name
    d.paired_at = datetime.now(timezone.utc).isoformat()
    db.commit()

    return PairResponse(success=True, device_id=d.device_id, message="Device paired successfully")


@router.get("/config/{device_id}", response_model=DeviceConfigResponse)
def device_config(device_id: str, db: Session = Depends(get_session)):
    d = db.get(Device, device_id)
    if not d or not d.paired:
        raise HTTPException(status_code=404, detail="Device not found or not yet paired")
    return DeviceConfigResponse(
        device_id=d.device_id,
        mac_address=d.mac_address,
        server_url=d.server_url or "",
        username=d.username or "",
        password=d.password or "",
        profile_name=d.profile_name or "",
        paired_at=d.paired_at or "",
    )


@router.delete("/unpair/{device_id}")
def unpair_device(device_id: str, db: Session = Depends(get_session)):
    d = db.get(Device, device_id)
    if not d:
        raise HTTPException(status_code=404, detail="Device not found")
    d.paired = False
    d.server_url = None
    d.username = None
    d.password = None
    d.profile_name = None
    d.paired_at = None
    # new PIN for re-pairing
    for _ in range(20):
        new_pin = generate_pin()
        if not db.scalar(select(Device).where(Device.pin == new_pin, Device.device_id != device_id)):
            d.pin = new_pin
            break
    db.commit()
    return {"success": True, "device_id": device_id, "new_pin": d.pin}
