"""Printers API routes."""

from flask import Blueprint, request
from flask_jwt_extended import jwt_required

from shopdb.extensions import db
from shopdb.utils.responses import success_response, error_response, paginated_response, ErrorCodes
from shopdb.utils.pagination import get_pagination_params, paginate_query
from shopdb.core.models.machine import Machine, MachineType
from shopdb.core.models.communication import Communication, CommunicationType
from shopdb.core.models import AuditLog

from ..models import PrinterData
from ..services import ZabbixService

printers_bp = Blueprint('printers', __name__)


@printers_bp.route('/', methods=['GET'])
@jwt_required(optional=True)
def list_printers():
    """List all printers."""
    page, per_page = get_pagination_params(request)

    # Get printer machine types
    printer_types = MachineType.query.filter_by(category='Printer').all()
    printer_type_ids = [pt.machinetypeid for pt in printer_types]

    query = Machine.query.filter(
        Machine.machinetypeid.in_(printer_type_ids),
        Machine.isactive == True
    )

    # Filters
    if location_id := request.args.get('location', type=int):
        query = query.filter(Machine.locationid == location_id)

    if search := request.args.get('search'):
        query = query.filter(
            db.or_(
                Machine.machinenumber.ilike(f'%{search}%'),
                Machine.hostname.ilike(f'%{search}%'),
                Machine.alias.ilike(f'%{search}%')
            )
        )

    query = query.order_by(Machine.machinenumber)
    items, total = paginate_query(query, page, per_page)

    printers = []
    for machine in items:
        printer_data = {
            'machineid': machine.machineid,
            'machinenumber': machine.machinenumber,
            'hostname': machine.hostname,
            'alias': machine.alias,
            'serialnumber': machine.serialnumber,
            'location': machine.location.locationname if machine.location else None,
            'vendor': machine.vendor.vendor if machine.vendor else None,
            'model': machine.model.modelnumber if machine.model else None,
            'status': machine.status.status if machine.status else None,
        }

        # Add printer-specific data
        if machine.printerdata:
            pd = machine.printerdata
            printer_data['printerdata'] = {
                'windowsname': pd.windowsname,
                'sharename': pd.sharename,
                'iscsf': pd.iscsf,
                'pin': pd.pin,
            }

        # Get IP from communications
        primary_comm = next((c for c in machine.communications if c.isprimary), None)
        if not primary_comm and machine.communications:
            primary_comm = machine.communications[0]
        printer_data['ipaddress'] = primary_comm.ipaddress if primary_comm else None

        printers.append(printer_data)

    return paginated_response(printers, page, per_page, total)


@printers_bp.route('/<int:machine_id>', methods=['GET'])
@jwt_required(optional=True)
def get_printer(machine_id: int):
    """Get a single printer with details."""
    machine = Machine.query.get(machine_id)

    if not machine:
        return error_response(ErrorCodes.NOT_FOUND, 'Printer not found', http_code=404)

    data = machine.to_dict()
    data['machinetype'] = machine.machinetype.to_dict() if machine.machinetype else None
    data['vendor'] = machine.vendor.to_dict() if machine.vendor else None
    data['model'] = machine.model.to_dict() if machine.model else None
    data['location'] = machine.location.to_dict() if machine.location else None
    data['status'] = machine.status.to_dict() if machine.status else None
    data['communications'] = [c.to_dict() for c in machine.communications]

    # Add printer-specific data
    if machine.printerdata:
        pd = machine.printerdata
        data['printerdata'] = {
            'id': pd.id,
            'windowsname': pd.windowsname,
            'sharename': pd.sharename,
            'iscsf': pd.iscsf,
            'installpath': pd.installpath,
            'pin': pd.pin,
        }

    return success_response(data)


@printers_bp.route('/<int:machine_id>/printerdata', methods=['PUT'])
@jwt_required()
def update_printer_data(machine_id: int):
    """Update printer-specific data."""
    machine = Machine.query.get(machine_id)

    if not machine:
        return error_response(ErrorCodes.NOT_FOUND, 'Printer not found', http_code=404)

    data = request.get_json()
    if not data:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided')

    # Get or create printer data
    pd = machine.printerdata
    if not pd:
        pd = PrinterData(machineid=machine_id)
        db.session.add(pd)

    # Track changes for audit log
    changes = {}
    for key in ['windowsname', 'sharename', 'iscsf', 'installpath', 'pin']:
        if key in data:
            old_val = getattr(pd, key, None)
            new_val = data[key]
            if old_val != new_val:
                changes[key] = {'old': old_val, 'new': new_val}
            setattr(pd, key, data[key])

    # Audit log if there were changes
    if changes:
        AuditLog.log('updated', 'Printer', entityid=machine_id,
                     entityname=machine.machinenumber or machine.hostname, changes=changes)

    db.session.commit()

    return success_response({
        'id': pd.id,
        'windowsname': pd.windowsname,
        'sharename': pd.sharename,
        'iscsf': pd.iscsf,
        'installpath': pd.installpath,
        'pin': pd.pin,
    }, message='Printer data updated')


@printers_bp.route('/<int:machine_id>/communication', methods=['PUT'])
@jwt_required()
def update_printer_communication(machine_id: int):
    """Update printer communication (IP address)."""
    machine = Machine.query.get(machine_id)

    if not machine:
        return error_response(ErrorCodes.NOT_FOUND, 'Printer not found', http_code=404)

    data = request.get_json()
    if not data:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided')

    # Get or create IP communication type
    ip_comtype = CommunicationType.query.filter_by(comtype='IP').first()
    if not ip_comtype:
        ip_comtype = CommunicationType(comtype='IP', description='IP Network')
        db.session.add(ip_comtype)
        db.session.flush()

    # Find existing primary communication or create new one
    comm = next((c for c in machine.communications if c.isprimary), None)
    if not comm:
        comm = next((c for c in machine.communications if c.comtypeid == ip_comtype.comtypeid), None)
    if not comm:
        comm = Communication(machineid=machine_id, comtypeid=ip_comtype.comtypeid)
        db.session.add(comm)

    # Track changes for audit log
    changes = {}

    # Update fields
    if 'ipaddress' in data:
        if comm.ipaddress != data['ipaddress']:
            changes['ipaddress'] = {'old': comm.ipaddress, 'new': data['ipaddress']}
        comm.ipaddress = data['ipaddress']
    if 'isprimary' in data:
        if comm.isprimary != data['isprimary']:
            changes['isprimary'] = {'old': comm.isprimary, 'new': data['isprimary']}
        comm.isprimary = data['isprimary']
    if 'macaddress' in data:
        if comm.macaddress != data['macaddress']:
            changes['macaddress'] = {'old': comm.macaddress, 'new': data['macaddress']}
        comm.macaddress = data['macaddress']

    # Audit log if there were changes
    if changes:
        AuditLog.log('updated', 'Printer', entityid=machine_id,
                     entityname=machine.machinenumber or machine.hostname, changes=changes)

    db.session.commit()

    return success_response({
        'communicationid': comm.communicationid,
        'ipaddress': comm.ipaddress,
        'isprimary': comm.isprimary,
    }, message='Communication updated')


@printers_bp.route('/<int:machine_id>/supplies', methods=['GET'])
@jwt_required(optional=True)
def get_printer_supplies(machine_id: int):
    """Get supply levels from Zabbix (real-time lookup)."""
    machine = Machine.query.get(machine_id)

    if not machine:
        return error_response(ErrorCodes.NOT_FOUND, 'Printer not found', http_code=404)

    # Get IP address
    primary_comm = next((c for c in machine.communications if c.isprimary), None)
    if not primary_comm and machine.communications:
        primary_comm = machine.communications[0]

    if not primary_comm or not primary_comm.ipaddress:
        return error_response(ErrorCodes.VALIDATION_ERROR, 'Printer has no IP address')

    service = ZabbixService()
    if not service.isconfigured or not service.isreachable:
        # Return empty supplies if Zabbix not available (fail gracefully)
        return success_response({
            'ipaddress': primary_comm.ipaddress,
            'supplies': []
        })

    supplies = service.getsuppliesbyip(primary_comm.ipaddress)

    return success_response({
        'ipaddress': primary_comm.ipaddress,
        'supplies': supplies or []
    })


@printers_bp.route('/dashboard/summary', methods=['GET'])
@jwt_required(optional=True)
def dashboard_summary():
    """Get printer summary for dashboard."""
    printer_types = MachineType.query.filter_by(category='Printer').all()
    printer_type_ids = [pt.machinetypeid for pt in printer_types]

    total = Machine.query.filter(
        Machine.machinetypeid.in_(printer_type_ids),
        Machine.isactive == True
    ).count()

    return success_response({
        'totalprinters': total,
        'total': total,
        'online': total,  # Placeholder - would need Zabbix integration for real status
        'lowsupplies': 0,
        'criticalsupplies': 0
    })
