import uuid
import os
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import FileResponse
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select

from app.core.database import get_db
from app.core.security import require_permission, hash_password, get_current_user
from app.models.core_models import LoginUsers
from app.models.access_models import RoleMaster, ModuleMaster, RoleModulePermission
from . import schemas

router = APIRouter()


# ── Modules ──────────────────────────────────────────────────────────
@router.get('/modules', response_model=list[schemas.ModuleOut])
async def list_modules(
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('USER_MANAGEMENT', 'view')),
):
    result = await db.execute(select(ModuleMaster).order_by(ModuleMaster.module_name))
    return result.scalars().all()


@router.patch('/modules/{module_code}', response_model=schemas.ModuleOut)
async def toggle_module(
    module_code: str,
    payload: schemas.ModuleToggle,
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('USER_MANAGEMENT', 'edit')),
):
    module = (await db.execute(
        select(ModuleMaster).where(ModuleMaster.module_code == module_code)
    )).scalar_one_or_none()
    if not module:
        raise HTTPException(status_code=404, detail="Module not found")
    module.is_active = payload.is_active
    await db.commit()
    await db.refresh(module)
    return module


# ── Roles ────────────────────────────────────────────────────────────
DEFAULT_OPERATOR_ROLES = [
    ("YARD_SUPERVISOR", "Yard Supervisor", "Oversees Yard operations, Chassis Inward, Station Scanning & Reports"),
    ("PRODUCTION_SUPERVISOR", "Production Supervisor", "Shopfloor station scanning, timers & chassis flow control"),
]

async def ensure_operator_roles(db: AsyncSession):
    changed = False
    for code, name, desc in DEFAULT_OPERATOR_ROLES:
        existing = (await db.execute(select(RoleMaster).where(RoleMaster.role_code == code))).scalar_one_or_none()
        if not existing:
            db.add(RoleMaster(
                role_id=uuid.uuid4(),
                role_code=code,
                role_name=name,
                description=desc,
                is_system_role=False,
                is_active=True,
            ))
            changed = True

        # Ensure default permissions
        default_modules = ["YARD"] if code == "YARD_SUPERVISOR" else ["PRODUCTION", "STATION"]
        for mod in default_modules:
            perm = (await db.execute(
                select(RoleModulePermission).where(
                    RoleModulePermission.role_code == code,
                    RoleModulePermission.module_code == mod
                )
            )).scalar_one_or_none()
            if not perm:
                db.add(RoleModulePermission(
                    id=uuid.uuid4(),
                    role_code=code,
                    module_code=mod,
                    can_view=True,
                    can_create=True,
                    can_edit=True,
                    can_delete=False,
                    can_approve=True if code == "YARD_SUPERVISOR" else False,
                ))
                changed = True
    if changed:
        await db.commit()


@router.get('/roles', response_model=list[schemas.RoleOut])
async def list_roles(
    db: AsyncSession = Depends(get_db),
    current_user: LoginUsers = Depends(get_current_user),
    _=Depends(require_permission('USER_MANAGEMENT', 'view')),
):
    await ensure_operator_roles(db)
    query = select(RoleMaster)
    if current_user.role_code != 'ADMIN':
        query = query.where(RoleMaster.role_code != 'ADMIN')
    result = await db.execute(query.order_by(RoleMaster.role_name))
    return result.scalars().all()


@router.post('/roles', response_model=schemas.RoleOut, status_code=201)
async def create_role(
    payload: schemas.RoleCreate,
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('USER_MANAGEMENT', 'create')),
):
    existing = (await db.execute(
        select(RoleMaster).where(RoleMaster.role_code == payload.role_code)
    )).scalar_one_or_none()
    if existing:
        raise HTTPException(status_code=400, detail="Role code already exists")
    role = RoleMaster(
        role_id=uuid.uuid4(),
        role_code=payload.role_code.upper().replace(' ', '_'),
        role_name=payload.role_name,
        description=payload.description,
        is_system_role=False,
        is_active=True,
    )
    db.add(role)
    await db.commit()
    await db.refresh(role)
    return role


# ── Permission matrix ────────────────────────────────────────────────
@router.get('/matrix', response_model=list[schemas.PermissionMatrixRow])
async def get_matrix(
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('USER_MANAGEMENT', 'view')),
):
    result = await db.execute(select(RoleModulePermission))
    return result.scalars().all()


@router.put('/matrix', response_model=schemas.PermissionMatrixRow)
async def upsert_matrix_row(
    payload: schemas.PermissionMatrixRow,
    db: AsyncSession = Depends(get_db),
    _=Depends(require_permission('USER_MANAGEMENT', 'edit')),
):
    row = (await db.execute(
        select(RoleModulePermission).where(
            RoleModulePermission.role_code == payload.role_code,
            RoleModulePermission.module_code == payload.module_code,
        )
    )).scalar_one_or_none()
    if row is None:
        row = RoleModulePermission(
            id=uuid.uuid4(), role_code=payload.role_code, module_code=payload.module_code,
        )
        db.add(row)
    row.can_view = payload.can_view
    row.can_create = payload.can_create
    row.can_edit = payload.can_edit
    row.can_delete = payload.can_delete
    row.can_approve = payload.can_approve
    await db.commit()
    await db.refresh(row)
    return row


# ── Users ────────────────────────────────────────────────────────────
@router.get('/users', response_model=list[schemas.UserOut])
async def list_users(
    db: AsyncSession = Depends(get_db),
    current_user: LoginUsers = Depends(get_current_user),
    _=Depends(require_permission('USER_MANAGEMENT', 'view')),
):
    query = select(LoginUsers)
    if current_user.role_code != 'ADMIN':
        query = query.where(LoginUsers.role_code != 'ADMIN')
    result = await db.execute(query.order_by(LoginUsers.name))
    return result.scalars().all()


@router.post('/users', response_model=schemas.UserOut, status_code=201)
async def create_user(
    payload: schemas.UserCreate,
    db: AsyncSession = Depends(get_db),
    current_user: LoginUsers = Depends(get_current_user),
    _=Depends(require_permission('USER_MANAGEMENT', 'create')),
):
    if current_user.role_code != 'ADMIN' and payload.role_code == 'ADMIN':
        raise HTTPException(status_code=403, detail="Cannot assign ADMIN role")
    existing = (await db.execute(
        select(LoginUsers).where(LoginUsers.employee_code == payload.employee_code)
    )).scalar_one_or_none()
    if existing:
        raise HTTPException(status_code=400, detail="Employee code already exists")
    user = LoginUsers(
        user_id=uuid.uuid4(),
        employee_code=payload.employee_code,
        name=payload.name,
        mobile=payload.mobile,
        email=payload.email,
        role_code=payload.role_code,
        password_hash=hash_password(payload.password),
        is_active=True,
    )
    db.add(user)
    await db.commit()
    await db.refresh(user)
    return user


@router.put('/users/{user_id}', response_model=schemas.UserOut)
async def update_user(
    user_id: uuid.UUID,
    payload: schemas.UserUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: LoginUsers = Depends(get_current_user),
    _=Depends(require_permission('USER_MANAGEMENT', 'edit')),
):
    user = await db.get(LoginUsers, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    if current_user.role_code != 'ADMIN' and user.role_code == 'ADMIN':
        raise HTTPException(status_code=403, detail="Cannot edit ADMIN users")
    if current_user.role_code != 'ADMIN' and getattr(payload, 'role_code', None) == 'ADMIN':
        raise HTTPException(status_code=403, detail="Cannot assign ADMIN role")
    for field, value in payload.model_dump(exclude_unset=True).items():
        setattr(user, field, value)
    await db.commit()
    await db.refresh(user)
    return user


@router.put('/users/{user_id}/password')
async def reset_password(
    user_id: uuid.UUID,
    payload: schemas.PasswordReset,
    db: AsyncSession = Depends(get_db),
    current_user: LoginUsers = Depends(get_current_user),
    _=Depends(require_permission('USER_MANAGEMENT', 'edit')),
):
    user = await db.get(LoginUsers, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    if current_user.role_code != 'ADMIN' and user.role_code == 'ADMIN':
        raise HTTPException(status_code=403, detail="Cannot reset password of ADMIN users")
    user.password_hash = hash_password(payload.new_password)
    await db.commit()
    return {"message": "Password updated"}

@router.delete('/users/{user_id}', status_code=204)
async def delete_user(
    user_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    current_user: LoginUsers = Depends(get_current_user),
    _=Depends(require_permission('USER_MANAGEMENT', 'delete')),
):
    user = await db.get(LoginUsers, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    if user.role_code == 'ADMIN':
        raise HTTPException(status_code=403, detail="Cannot delete ADMIN users")
    await db.delete(user)
    await db.commit()
    return None


# ── System Logs ──────────────────────────────────────────────────────
@router.get('/logs')
async def get_system_logs(
    lines: int = 100,
    _=Depends(require_permission('SYSTEM_LOGS', 'view')),
):
    log_path = os.path.join(os.getcwd(), "logs", "error.log")
    if not os.path.exists(log_path):
        return {"logs": "No log file found."}
    
    try:
        with open(log_path, 'r', encoding='utf-8') as f:
            all_lines = f.readlines()
            return {"logs": "".join(all_lines[-lines:])}
    except Exception as e:
        return {"logs": f"Error reading log file: {str(e)}"}


@router.get('/logs/download')
async def download_system_logs(
    _=Depends(require_permission('SYSTEM_LOGS', 'view')),
):
    log_path = os.path.join(os.getcwd(), "logs", "error.log")
    if not os.path.exists(log_path):
        raise HTTPException(status_code=404, detail="Log file not found.")
    
    return FileResponse(
        path=log_path,
        filename="error.log",
        media_type="text/plain"
    )
