"""Approval Workflow API.

Replaces the earlier raw-SQL implementation with a standard Odoo ORM
controller. Backing models live in ``encoach_exam_template/models/approval.py``.

Serves the admin "Approval Config" page (``/admin/approval-config``).
"""
import logging
from datetime import datetime

from odoo import http
from odoo.http import request
from odoo.addons.encoach_api.controllers.base import (
    jwt_required, _json_response, _error_response, _get_json_body,
)

_logger = logging.getLogger(__name__)


# ── Serializers ─────────────────────────────────────────────────────────────

def _ser_stage(stage):
    """Stages are written as ``sequence`` (ORM) but the admin UI speaks
    ``order`` (1-based, dense). We emit both so either side works."""
    return {
        'id': stage.id,
        'order': stage.sequence,
        'sequence': stage.sequence,
        'approver_id': stage.approver_id.id or None,
        'approver_name': stage.approver_id.name if stage.approver_id else '',
        'max_days': stage.max_days or 0,
        'auto_escalate': bool(stage.auto_escalate),
        'notification_email': stage.notification_email or '',
        'status': stage.status or 'pending',
        'comment': stage.comment or '',
        'acted_at': stage.acted_at.isoformat() if stage.acted_at else None,
    }


def _ser_workflow(wf):
    return {
        'id': wf.id,
        'name': wf.name or '',
        'type': wf.type or 'custom',
        'status': wf.status or 'draft',
        'allow_bypass': bool(wf.allow_bypass),
        'entity_id': wf.entity_id.id or None,
        'entity_name': wf.entity_id.name if wf.entity_id else None,
        'steps': [_ser_stage(s) for s in wf.stage_ids.sorted('sequence')],
        'stage_count': wf.stage_count,
        'request_count': wf.request_count,
        'created_at': wf.create_date.isoformat() if wf.create_date else None,
        # Legacy shape kept for any older callers that read `created`.
        'created': wf.create_date.strftime('%Y-%m-%d') if wf.create_date else '',
    }


def _ser_request(req):
    stage = req.current_stage_id
    return {
        'id': req.id,
        'workflow_id': req.workflow_id.id or None,
        'workflow_name': req.workflow_id.name if req.workflow_id else '',
        'res_model': req.res_model or '',
        'res_id': req.res_id or 0,
        'state': req.state or 'draft',
        'requester_id': req.requester_id.id or None,
        'requester_name': req.requester_id.name if req.requester_id else '',
        'current_stage_id': stage.id or None,
        'current_stage_sequence': stage.sequence if stage else None,
        'bypass_reason': req.bypass_reason or '',
        'created_at': req.created_at.isoformat() if req.created_at else None,
    }


# ── Write helpers ───────────────────────────────────────────────────────────

def _apply_workflow_writes(body, vals):
    for k in ('name', 'type'):
        if k in body and body[k] is not None:
            vals[k] = body[k]
    if 'status' in body and body['status'] in ('draft', 'active', 'archived'):
        vals['status'] = body['status']
    if 'allow_bypass' in body:
        vals['allow_bypass'] = bool(body['allow_bypass'])
    # Frontend sends `bypass_enabled` from the workflow-builder dialog.
    if 'bypass_enabled' in body:
        vals['allow_bypass'] = bool(body['bypass_enabled'])
    if 'entity_id' in body:
        vals['entity_id'] = int(body['entity_id']) if body['entity_id'] else False
    return vals


def _stage_vals_from_body(step, default_seq):
    """Accept either `order` (UI) or `sequence` (ORM). Emit `sequence` only."""
    seq = step.get('sequence')
    if seq is None:
        seq = step.get('order', default_seq)
    try:
        seq = int(seq)
    except (TypeError, ValueError):
        seq = default_seq
    approver_id = step.get('approver_id')
    try:
        approver_id = int(approver_id) if approver_id else 0
    except (TypeError, ValueError):
        approver_id = 0
    vals = {
        'sequence': seq,
        'approver_id': approver_id or False,
        'max_days': int(step.get('max_days') or 3),
        'auto_escalate': bool(step.get('auto_escalate')),
        'notification_email': step.get('notification_email') or False,
    }
    return vals


# ── Controller ──────────────────────────────────────────────────────────────

class ApprovalWorkflowController(http.Controller):

    # ── Workflows ────────────────────────────────────────────────

    @http.route('/api/approval-workflows', type='http', auth='none',
                methods=['GET'], csrf=False)
    @jwt_required
    def list_workflows(self, **kw):
        try:
            M = request.env['encoach.approval.workflow'].sudo()
            domain = []
            if kw.get('status'):
                domain.append(('status', '=', kw['status']))
            if kw.get('entity_id'):
                domain.append(('entity_id', '=', int(kw['entity_id'])))
            recs = M.search(domain, order='create_date desc')
            items = [_ser_workflow(r) for r in recs]
            # Envelope supports both {items,total} (legacy) and {results,total}
            # (PaginatedResponse shape used by the approvals frontend service).
            return _json_response({
                'items': items,
                'results': items,
                'total': len(items),
            })
        except Exception as e:
            _logger.exception('list approval workflows failed')
            return _error_response(str(e), 500)

    @http.route('/api/approval-workflows', type='http', auth='none',
                methods=['POST'], csrf=False)
    @jwt_required
    def create_workflow(self, **kw):
        try:
            body = _get_json_body()
            name = (body.get('name') or '').strip()
            if not name:
                return _error_response('name is required', 400)

            vals = {'name': name, 'type': body.get('type', 'custom')}
            _apply_workflow_writes(body, vals)

            steps = body.get('steps') or []
            stage_cmds = []
            for i, step in enumerate(steps):
                sv = _stage_vals_from_body(step, (i + 1) * 10)
                if sv.get('approver_id'):
                    stage_cmds.append((0, 0, sv))
            if stage_cmds:
                vals['stage_ids'] = stage_cmds

            rec = request.env['encoach.approval.workflow'].sudo().create(vals)
            return _json_response(_ser_workflow(rec), 201)
        except Exception as e:
            _logger.exception('create approval workflow failed')
            return _error_response(str(e), 500)

    @http.route('/api/approval-workflows/<int:wf_id>', type='http',
                auth='none', methods=['GET'], csrf=False)
    @jwt_required
    def get_workflow(self, wf_id, **kw):
        try:
            rec = request.env['encoach.approval.workflow'].sudo().browse(wf_id)
            if not rec.exists():
                return _error_response('Not found', 404)
            return _json_response(_ser_workflow(rec))
        except Exception as e:
            _logger.exception('get approval workflow failed')
            return _error_response(str(e), 500)

    @http.route('/api/approval-workflows/<int:wf_id>', type='http',
                auth='none', methods=['PATCH', 'PUT'], csrf=False)
    @jwt_required
    def update_workflow(self, wf_id, **kw):
        try:
            rec = request.env['encoach.approval.workflow'].sudo().browse(wf_id)
            if not rec.exists():
                return _error_response('Not found', 404)
            body = _get_json_body()
            vals = {}
            _apply_workflow_writes(body, vals)

            # Replace stages only when steps key is present in the body so
            # a status-only toggle doesn't wipe stages.
            if 'steps' in body and isinstance(body['steps'], list):
                cmds = [(5, 0, 0)]
                for i, step in enumerate(body['steps']):
                    sv = _stage_vals_from_body(step, (i + 1) * 10)
                    if sv.get('approver_id'):
                        cmds.append((0, 0, sv))
                vals['stage_ids'] = cmds

            if vals:
                rec.write(vals)
            return _json_response(_ser_workflow(rec))
        except Exception as e:
            _logger.exception('update approval workflow failed')
            return _error_response(str(e), 500)

    @http.route('/api/approval-workflows/<int:wf_id>', type='http',
                auth='none', methods=['DELETE'], csrf=False)
    @jwt_required
    def delete_workflow(self, wf_id, **kw):
        try:
            rec = request.env['encoach.approval.workflow'].sudo().browse(wf_id)
            if rec.exists():
                rec.unlink()
            return _json_response({'success': True})
        except Exception as e:
            _logger.exception('delete approval workflow failed')
            return _error_response(str(e), 500)

    # ── Approval Requests ────────────────────────────────────────

    @http.route('/api/approval-requests', type='http', auth='none',
                methods=['GET'], csrf=False)
    @jwt_required
    def list_requests(self, **kw):
        try:
            M = request.env['encoach.approval.request'].sudo()
            domain = []
            if kw.get('state'):
                domain.append(('state', '=', kw['state']))
            if kw.get('workflow_id'):
                domain.append(('workflow_id', '=', int(kw['workflow_id'])))
            recs = M.search(domain, order='created_at desc, id desc')
            items = [_ser_request(r) for r in recs]
            return _json_response({
                'items': items,
                'results': items,
                'total': len(items),
            })
        except Exception as e:
            _logger.exception('list approval requests failed')
            return _error_response(str(e), 500)

    @http.route('/api/approval-requests/<int:req_id>/approve', type='http',
                auth='none', methods=['POST'], csrf=False)
    @jwt_required
    def approve_request(self, req_id, **kw):
        try:
            body = _get_json_body()
            req_rec = request.env['encoach.approval.request'].sudo().browse(req_id)
            if not req_rec.exists():
                return _error_response('Request not found', 404)

            stage = req_rec.current_stage_id
            if stage:
                stage.write({
                    'status': 'approved',
                    'comment': body.get('comment', ''),
                    'acted_at': datetime.now(),
                })

            # Move to next stage or finalize.
            stages = req_rec.workflow_id.stage_ids.sorted('sequence')
            stage_ids = [s.id for s in stages]
            try:
                idx = stage_ids.index(stage.id) if stage else -1
            except ValueError:
                idx = -1

            if 0 <= idx and idx + 1 < len(stage_ids):
                next_stage = request.env['encoach.approval.stage'].sudo().browse(stage_ids[idx + 1])
                req_rec.write({
                    'current_stage_id': next_stage.id,
                    'state': 'in_progress',
                })
            else:
                req_rec.write({'state': 'approved'})

            return _json_response({'success': True, 'id': req_id,
                                   'state': req_rec.state})
        except Exception as e:
            _logger.exception('approve request failed')
            return _error_response(str(e), 500)

    @http.route('/api/approval-requests/<int:req_id>/reject', type='http',
                auth='none', methods=['POST'], csrf=False)
    @jwt_required
    def reject_request(self, req_id, **kw):
        try:
            body = _get_json_body()
            req_rec = request.env['encoach.approval.request'].sudo().browse(req_id)
            if not req_rec.exists():
                return _error_response('Request not found', 404)
            stage = req_rec.current_stage_id
            if stage:
                stage.write({
                    'status': 'rejected',
                    'comment': body.get('comment', ''),
                    'acted_at': datetime.now(),
                })
            req_rec.write({'state': 'rejected'})
            return _json_response({'success': True, 'id': req_id,
                                   'state': req_rec.state})
        except Exception as e:
            _logger.exception('reject request failed')
            return _error_response(str(e), 500)

    # ── Approvers picker ─────────────────────────────────────────

    @http.route('/api/approval-users', type='http', auth='none',
                methods=['GET'], csrf=False)
    @jwt_required
    def list_users(self, **kw):
        try:
            Users = request.env['res.users'].sudo()
            domain = [('active', '=', True), ('id', '>', 1)]
            search = (kw.get('search') or '').strip()
            if search:
                domain += ['|', ('name', 'ilike', search), ('login', 'ilike', search)]
            recs = Users.search(domain, order='name', limit=100)
            items = [{
                'id': u.id,
                'name': u.name or u.login,
                'login': u.login or '',
                'email': u.email or '',
            } for u in recs]
            return _json_response({'items': items, 'results': items, 'total': len(items)})
        except Exception as e:
            _logger.exception('list approval users failed')
            return _error_response(str(e), 500)
