from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import delete, desc, select
from sqlalchemy.orm import Session

from app.db.session import get_db
from app.models.miniapp import (
    MiniAppAttendancePoint,
    MiniAppNotice,
    MiniAppNoticePoint,
    MiniAppPersonRole,
    MiniAppPersonnel,
)
from app.models.org import AiAssistantConfig, SystemConfig
from app.schemas.database import (
    AiAssistantConfigRead,
    AiAssistantConfigUpdate,
    BroadcastMessageCreate,
    BroadcastMessageRead,
    SmartOperationReportConfigRead,
    SmartOperationReportConfigUpdate,
    SystemConfigRead,
    SystemConfigUpdate,
)
from app.services.auth import AuthContext, require_authenticated_user, require_system_admin
from app.services.system_config import (
    RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE,
    RAW_MATERIAL_LOT_PREFIX_DEFAULT,
    SMART_OPERATION_REPORT_CONFIG_CODE,
    get_smart_operation_report_enabled,
    normalize_enabled_config_value,
    upsert_smart_operation_report_config,
    upsert_system_config,
)

router = APIRouter()


def _active_attendance_point_names(db: Session) -> list[str]:
    return [
        name
        for name in db.scalars(
            select(MiniAppAttendancePoint.name)
            .where(MiniAppAttendancePoint.is_active.is_(True))
            .order_by(MiniAppAttendancePoint.name.asc())
        ).all()
        if str(name or "").strip()
    ]


def _notice_point_names(db: Session, notice_id: int) -> list[str]:
    return [
        name
        for name in db.scalars(
            select(MiniAppNoticePoint.attendance_point_name)
            .where(MiniAppNoticePoint.notice_id == notice_id)
            .order_by(MiniAppNoticePoint.attendance_point_name.asc())
        ).all()
        if str(name or "").strip()
    ]


def _replace_notice_points(db: Session, notice_id: int, point_names: list[str] | None) -> None:
    selected_names = [str(name or "").strip() for name in point_names or _active_attendance_point_names(db) if str(name or "").strip()]
    selected_names = list(dict.fromkeys(selected_names))
    if selected_names:
        existing_names = set(
            db.scalars(select(MiniAppAttendancePoint.name).where(MiniAppAttendancePoint.name.in_(selected_names))).all()
        )
        missing = [name for name in selected_names if name not in existing_names]
        if missing:
            raise HTTPException(status_code=400, detail=f"小程序考勤点不存在：{'、'.join(missing)}")
    db.execute(delete(MiniAppNoticePoint).where(MiniAppNoticePoint.notice_id == notice_id))
    for name in selected_names:
        db.add(MiniAppNoticePoint(notice_id=notice_id, attendance_point_name=name))


def _broadcast_read(db: Session, row: MiniAppNotice) -> BroadcastMessageRead:
    return BroadcastMessageRead(
        broadcast_id=row.id,
        title=row.title,
        content=row.content,
        attendance_point_names=_notice_point_names(db, row.id),
        tone="INFO",
        status="ACTIVE" if row.is_active else "PAUSED",
        starts_at=None,
        ends_at=None,
        sort_no=row.sort_order,
        created_by=row.created_by,
        created_at=row.created_at,
        updated_at=row.updated_at,
    )


def _get_notice_creator_phone(db: Session) -> str:
    admin_phone = db.scalar(
        select(MiniAppPersonRole.phone)
        .where(MiniAppPersonRole.role.in_(["admin", "manager"]))
        .order_by(MiniAppPersonRole.role, MiniAppPersonRole.phone)
        .limit(1)
    )
    if admin_phone:
        return admin_phone
    first_phone = db.scalar(select(MiniAppPersonnel.phone).order_by(MiniAppPersonnel.phone).limit(1))
    if first_phone:
        return first_phone

    system_person = MiniAppPersonnel(
        phone="ERP_SYSTEM",
        name="ERP系统",
        is_temporary=False,
        temporary_expires_at=None,
    )
    db.add(system_person)
    db.flush()
    db.add(MiniAppPersonRole(phone=system_person.phone, role="manager"))
    return system_person.phone


def _assistant_config_read(row: AiAssistantConfig | None) -> AiAssistantConfigRead:
    if not row:
        return AiAssistantConfigRead(
            assistant_name="嘉恒工艺助手",
            provider_name="",
            api_base_url="",
            api_key=None,
            model_name="",
            temperature=0.3,
            system_prompt="你是宁波嘉恒智能五金 ERP 的企业助手，优先解释业务流程、字段含义、数据异常和操作步骤。",
            enabled=False,
            config_id=None,
            api_key_configured=False,
            updated_at=None,
        )

    return AiAssistantConfigRead(
        config_id=row.id,
        assistant_name=row.assistant_name,
        provider_name=row.provider_name,
        api_base_url=row.api_base_url,
        api_key=None,
        model_name=row.model_name,
        temperature=float(row.temperature or 0.3),
        system_prompt=row.system_prompt,
        enabled=bool(row.enabled),
        api_key_configured=bool(row.api_key),
        updated_at=row.updated_at,
    )


def _raw_material_lot_prefix_read(row: SystemConfig | None = None) -> SystemConfigRead:
    if not row:
        return SystemConfigRead(
            config_code=RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE,
            config_name="原材料库存批次号前缀",
            config_value=RAW_MATERIAL_LOT_PREFIX_DEFAULT,
            remark="原材料库存批次号默认前缀，例如 YL0001",
            updated_at=None,
        )
    return SystemConfigRead.model_validate(row)


def _smart_operation_report_config_read(row: SystemConfig | None, db: Session) -> SmartOperationReportConfigRead:
    enabled = get_smart_operation_report_enabled(db) if row is None else normalize_enabled_config_value(row.config_value) == "开启"
    return SmartOperationReportConfigRead(
        config_code=SMART_OPERATION_REPORT_CONFIG_CODE,
        config_name="对接智能报工小程序",
        enabled=enabled,
        config_value="开启" if enabled else "关闭",
        remark=row.remark if row else "开启后显示工序报工并按小程序报工数据计算生产进度；关闭后按ERP入库数据反推生产进度",
        updated_at=row.updated_at if row else None,
    )


def _active_broadcast_rows(db: Session, limit: int = 30) -> list[MiniAppNotice]:
    return db.scalars(
        select(MiniAppNotice)
        .where(MiniAppNotice.is_active.is_(True))
        .order_by(MiniAppNotice.sort_order.asc(), desc(MiniAppNotice.updated_at), desc(MiniAppNotice.id))
        .limit(limit)
    ).all()


@router.get("/active-broadcast", response_model=BroadcastMessageRead | None)
def active_broadcast(
    context: AuthContext = Depends(require_authenticated_user),
    db: Session = Depends(get_db),
) -> BroadcastMessageRead | None:
    _ = context
    rows = _active_broadcast_rows(db, limit=1)
    row = rows[0] if rows else None
    return _broadcast_read(db, row) if row else None


@router.get("/active-broadcasts", response_model=list[BroadcastMessageRead])
def active_broadcasts(
    limit: int = Query(default=30, ge=1, le=100),
    context: AuthContext = Depends(require_authenticated_user),
    db: Session = Depends(get_db),
) -> list[BroadcastMessageRead]:
    _ = context
    return [_broadcast_read(db, row) for row in _active_broadcast_rows(db, limit=limit)]


@router.get("/broadcasts", response_model=list[BroadcastMessageRead])
def list_broadcasts(
    limit: int = Query(default=100, ge=1, le=300),
    context: AuthContext = Depends(require_system_admin),
    db: Session = Depends(get_db),
) -> list[BroadcastMessageRead]:
    _ = context
    rows = db.scalars(select(MiniAppNotice).order_by(MiniAppNotice.sort_order.asc(), desc(MiniAppNotice.updated_at)).limit(limit)).all()
    return [_broadcast_read(db, row) for row in rows]


@router.post("/broadcasts", response_model=BroadcastMessageRead)
def create_broadcast(
    payload: BroadcastMessageCreate,
    context: AuthContext = Depends(require_system_admin),
    db: Session = Depends(get_db),
) -> BroadcastMessageRead:
    creator_phone = _get_notice_creator_phone(db)
    row = MiniAppNotice(
        title=payload.title,
        content=payload.content,
        sort_order=payload.sort_no,
        is_active=payload.status == "ACTIVE",
        created_by=creator_phone,
    )
    db.add(row)
    db.flush()
    _replace_notice_points(db, row.id, payload.attendance_point_names)
    db.commit()
    db.refresh(row)
    return _broadcast_read(db, row)


@router.put("/broadcasts/{broadcast_id}", response_model=BroadcastMessageRead)
def update_broadcast(
    broadcast_id: int,
    payload: BroadcastMessageCreate,
    context: AuthContext = Depends(require_system_admin),
    db: Session = Depends(get_db),
) -> BroadcastMessageRead:
    _ = context
    row = db.get(MiniAppNotice, broadcast_id)
    if not row:
        raise HTTPException(status_code=404, detail="广播内容不存在")

    row.title = payload.title
    row.content = payload.content
    row.sort_order = payload.sort_no
    row.is_active = payload.status == "ACTIVE"
    db.add(row)
    _replace_notice_points(db, row.id, payload.attendance_point_names)
    db.commit()
    db.refresh(row)
    return _broadcast_read(db, row)


@router.get("/assistant-config", response_model=AiAssistantConfigRead)
def get_assistant_config(
    context: AuthContext = Depends(require_system_admin),
    db: Session = Depends(get_db),
) -> AiAssistantConfigRead:
    _ = context
    row = db.scalars(select(AiAssistantConfig).order_by(desc(AiAssistantConfig.id)).limit(1)).first()
    return _assistant_config_read(row)


@router.put("/assistant-config", response_model=AiAssistantConfigRead)
def save_assistant_config(
    payload: AiAssistantConfigUpdate,
    context: AuthContext = Depends(require_system_admin),
    db: Session = Depends(get_db),
) -> AiAssistantConfigRead:
    row = db.scalars(select(AiAssistantConfig).order_by(desc(AiAssistantConfig.id)).limit(1)).first()
    if not row:
        row = AiAssistantConfig(created_by=context.user.id)

    row.assistant_name = payload.assistant_name
    row.provider_name = payload.provider_name
    row.api_base_url = payload.api_base_url
    if payload.api_key is not None:
        row.api_key = payload.api_key
    row.model_name = payload.model_name
    row.temperature = payload.temperature
    row.system_prompt = payload.system_prompt
    row.enabled = 1 if payload.enabled else 0
    row.updated_by = context.user.id
    db.add(row)
    db.commit()
    db.refresh(row)
    return _assistant_config_read(row)


@router.get("/raw-material-lot-prefix", response_model=SystemConfigRead)
def get_raw_material_lot_prefix_config(
    context: AuthContext = Depends(require_system_admin),
    db: Session = Depends(get_db),
) -> SystemConfigRead:
    _ = context
    row = db.scalar(
        select(SystemConfig).where(
            SystemConfig.config_code == RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE,
            SystemConfig.status == "ACTIVE",
        )
    )
    return _raw_material_lot_prefix_read(row)


@router.put("/raw-material-lot-prefix", response_model=SystemConfigRead)
def save_raw_material_lot_prefix_config(
    payload: SystemConfigUpdate,
    context: AuthContext = Depends(require_system_admin),
    db: Session = Depends(get_db),
) -> SystemConfigRead:
    row = upsert_system_config(
        db,
        config_code=RAW_MATERIAL_LOT_PREFIX_CONFIG_CODE,
        config_name="原材料库存批次号前缀",
        config_value=payload.config_value,
        remark=payload.remark or "原材料库存批次号默认前缀，例如 YL0001",
        updated_by=context.user.id,
    )
    db.commit()
    db.refresh(row)
    return _raw_material_lot_prefix_read(row)


@router.get("/smart-operation-report", response_model=SmartOperationReportConfigRead)
def get_smart_operation_report_config(
    context: AuthContext = Depends(require_authenticated_user),
    db: Session = Depends(get_db),
) -> SmartOperationReportConfigRead:
    _ = context
    row = db.scalar(
        select(SystemConfig).where(
            SystemConfig.config_code == SMART_OPERATION_REPORT_CONFIG_CODE,
            SystemConfig.status == "ACTIVE",
        )
    )
    return _smart_operation_report_config_read(row, db)


@router.put("/smart-operation-report", response_model=SmartOperationReportConfigRead)
def save_smart_operation_report_config(
    payload: SmartOperationReportConfigUpdate,
    context: AuthContext = Depends(require_system_admin),
    db: Session = Depends(get_db),
) -> SmartOperationReportConfigRead:
    row = upsert_smart_operation_report_config(
        db,
        enabled=payload.enabled,
        updated_by=context.user.id,
        remark=payload.remark,
    )
    db.commit()
    db.refresh(row)
    return _smart_operation_report_config_read(row, db)
