"""
SOAP service para gestion de solicitudes de licencia.

Endpoints (montados en /SolicitudesService.asmx):
  - RegistrarSolicitud      -> crea fila Solicitudes(Estado='Pendiente')
  - ConsultarEstadoSolicitud-> retorna estado actual para polling del cliente MIG

El cliente MIG genera Web Reference contra ?wsdl y consume estos metodos.
Las credenciales SOAP heredadas son admin/123*/ (igual que el panel viejo).
"""
from __future__ import annotations

import os
from datetime import datetime
from typing import Optional

from flask import Flask, request
from spyne import Application, ServiceBase, Unicode, rpc
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
from werkzeug.wrappers import Response

from models import Solicitud, db
from config import Config


NS = 'http://migsistemas.com/licencias/solicitudes/'


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _flask_app() -> Flask:
    from flask import current_app
    return current_app._get_current_object()  # type: ignore[attr-defined]


def _autenticar(usuario: Optional[str], contrasenia: Optional[str]) -> bool:
    return (usuario or '') == Config.SOAP_USER and (contrasenia or '') == Config.SOAP_PASSWORD


def _ip_cliente() -> str:
    if request:
        return request.headers.get('X-Forwarded-For', request.remote_addr or '')
    return ''


# ---------------------------------------------------------------------------
# Servicio SOAP
# ---------------------------------------------------------------------------
class SolicitudesService(ServiceBase):
    """Endpoints expuestos al cliente MIG."""

    @rpc(
        Unicode, Unicode,                                       # usuario, contrasenia
        Unicode, Unicode, Unicode, Unicode, Unicode, Unicode,   # nit, razonsocial, nombrecomercial, direccion, telefono, celular
        Unicode, Unicode,                                       # departamento, ciudad
        Unicode,                                                # licenciatemporal (SI/NO)
        Unicode, Unicode, Unicode, Unicode,                     # responsable, correo, telefono_resp, motivo
        Unicode, Unicode, Unicode, Unicode,                     # claveproducto, clavemes, nombreequipo, osversion
        Unicode,                                                # modulo (MIG | MIGMESEROS | MIGADMINISTRATIVO)
        _returns=Unicode,
    )
    def RegistrarSolicitud(
        ctx,
        usuario, contrasenia,
        nit, razonsocial, nombrecomercial, direccion, telefono, celular,
        departamento, ciudad,
        licenciatemporal,
        responsable, correoresponsable, telefonoresponsable, motivo,
        claveproducto, clavemes, nombreequipo, osversion,
        modulo,
    ):
        """Crea una solicitud nueva en estado Pendiente.

        Retorna:
          OK|<SolicitudID>   -> al cliente para que sepa que ID polling
          ERROR|<mensaje>    -> si algo fallo
        """
        if not _autenticar(usuario, contrasenia):
            return 'ERROR|Credenciales invalidas'

        app = _flask_app()
        with app.app_context():
            try:
                solicitud = Solicitud(
                    NIT=nit or '',
                    NombreEmpresa=razonsocial,
                    NombreComercial=nombrecomercial,
                    Direccion=direccion,
                    Telefono=telefono,
                    Celular=celular,
                    Departamento=departamento,
                    Ciudad=ciudad,
                    Responsable=responsable,
                    TelefonoResponsable=telefonoresponsable,
                    CorreoResponsable=correoresponsable,
                    Motivo=motivo,
                    LicenciaTemporal=(licenciatemporal or 'NO').upper().strip(),
                    ClaveProducto=claveproducto or '',
                    ClaveMes=clavemes,
                    NombreEquipo=nombreequipo,
                    OSVersion=osversion,
                    Modulo=(modulo or 'MIG').upper().strip(),
                    FechaSolicitud=datetime.utcnow(),
                    Estado='Pendiente',
                    IpCliente=_ip_cliente(),
                )
                db.session.add(solicitud)
                db.session.commit()

                return f'OK|{solicitud.SolicitudID}'
            except Exception as e:
                db.session.rollback()
                return f'ERROR|{type(e).__name__}: {e}'

    @rpc(
        Unicode, Unicode,                       # usuario, contrasenia
        Unicode, Unicode, Unicode,              # nit, claveproducto, nombreequipo
        _returns=Unicode,
    )
    def ConsultarEstadoSolicitud(ctx, usuario, contrasenia, nit, claveproducto, nombreequipo):
        """Para polling desde el cliente MIG mientras espera aprobacion.

        Busca la solicitud mas reciente del (nit, claveproducto, nombreequipo)
        y retorna el estado actual.

        Formato de respuesta (separador pipe):
            <Estado>|<FechaResolucion>|<Observaciones>|<FechaVencimiento>

        Estados posibles: Pendiente | Aprobada | Rechazada | NoEncontrada | ERROR
        """
        if not _autenticar(usuario, contrasenia):
            return 'ERROR|Credenciales invalidas|||'

        app = _flask_app()
        with app.app_context():
            try:
                q = Solicitud.query.filter(
                    Solicitud.NIT == (nit or ''),
                    Solicitud.NombreEquipo == (nombreequipo or ''),
                    Solicitud.ClaveProducto == (claveproducto or ''),
                ).order_by(Solicitud.SolicitudID.desc())

                solicitud = q.first()

                if solicitud is None:
                    return 'NoEncontrada|||'

                fecha_res = (
                    solicitud.FechaResolucion.strftime('%Y-%m-%d %H:%M:%S')
                    if solicitud.FechaResolucion else ''
                )
                obs = (solicitud.Observaciones or '').replace('|', ' ')
                fecha_venc = (
                    solicitud.FechaVencimiento.strftime('%Y-%m-%d %H:%M:%S')
                    if solicitud.FechaVencimiento else ''
                )

                return f'{solicitud.Estado}|{fecha_res}|{obs}|{fecha_venc}'
            except Exception as e:
                return f'ERROR|{type(e).__name__}: {e}|||'


# ---------------------------------------------------------------------------
# Registro de la aplicacion Spyne en Flask
# ---------------------------------------------------------------------------
def register_soap(app: Flask) -> None:
    """Monta el endpoint SOAP en /SolicitudesService.asmx.

    Soporta:
      GET  ?wsdl  -> sirve el WSDL para wsdl.exe / svcutil
      POST        -> procesa sobres SOAP
    """
    spyne_app = Application(
        services=[SolicitudesService],
        tns=NS,
        in_protocol=Soap11(validator='lxml'),
        out_protocol=Soap11(),
    )
    wsgi_soap = WsgiApplication(spyne_app)

    @app.route('/SolicitudesService.asmx', methods=['GET', 'POST'])
    def solicitudes_asmx():
        if request.method == 'GET' and request.args.get('wsdl') is not None:
            environ = dict(request.environ)
            environ['QUERY_STRING'] = 'wsdl'
            return _wsgi_pass(environ, wsgi_soap)
        return _wsgi_pass(request.environ, wsgi_soap)


def _wsgi_pass(environ, wsgi_soap) -> Response:
    """Adapta la respuesta WSGI de Spyne para que Flask la pueda devolver."""
    holder = {'status': '', 'headers': []}

    def start_response(status, headers, exc_info=None):
        holder['status']  = status
        holder['headers'] = headers
        return lambda x: None

    body = b''.join(wsgi_soap(environ, start_response))
    status_code = int(holder['status'].split(' ', 1)[0]) if holder['status'] else 200
    return Response(body, status=status_code, headers=holder['headers'])
