import json
import logging
import math
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, _paginate
)
from odoo.addons.encoach_adaptive.services.style_matcher import STYLE_RESOURCE_MAP

_logger = logging.getLogger(__name__)


class EncoachAdaptiveController(http.Controller):

    # ------------------------------------------------------------------
    # GET /api/adaptive/dashboard
    # ------------------------------------------------------------------
    @http.route('/api/adaptive/dashboard', type='http', auth='none',
                methods=['GET'], csrf=False)
    @jwt_required
    def dashboard(self, **kw):
        try:
            Event = request.env['encoach.adaptive.event'].sudo()
            Path = request.env['encoach.adaptive.path'].sudo()

            from odoo.fields import Datetime as DT
            today_start = DT.now().replace(hour=0, minute=0, second=0, microsecond=0)

            all_paths = Path.search([])
            total_students = len(all_paths.mapped('student_id'))
            active_courses = len(all_paths.mapped('course_id').filtered(lambda c: c))

            signals_today = Event.search_count([
                ('event_type', '=', 'signal'),
                ('created_at', '>=', today_start),
            ])

            avg_progress = 0.0
            if all_paths:
                progress_values = []
                for p in all_paths:
                    try:
                        module_queue = json.loads(p.module_queue or '[]')
                    except (json.JSONDecodeError, TypeError):
                        module_queue = []
                    total_modules = len(module_queue) if module_queue else 1
                    completed = sum(
                        1 for m in module_queue
                        if isinstance(m, dict) and m.get('done')
                    )
                    progress_values.append(
                        round(completed / total_modules * 100, 1) if total_modules else 0.0
                    )
                avg_progress = round(sum(progress_values) / len(progress_values), 1) if progress_values else 0.0

            recent_decisions = []
            decisions = Event.search(
                [('event_type', '=', 'decision')],
                limit=10, order='created_at desc',
            )
            for d in decisions:
                recent_decisions.append({
                    'id': d.id,
                    'student_id': d.student_id.id,
                    'student_name': d.student_id.name or '',
                    'decision': d.decision or '',
                    'created_at': d.created_at,
                })

            return _json_response({
                'total_students': total_students,
                'active_courses': active_courses,
                'avg_progress': avg_progress,
                'signals_today': signals_today,
                'recent_decisions': recent_decisions,
            })

        except Exception as e:
            _logger.exception('adaptive dashboard failed')
            return _error_response(str(e), 500)

    # ------------------------------------------------------------------
    # GET /api/adaptive/students
    # ------------------------------------------------------------------
    @http.route('/api/adaptive/students', type='http', auth='none',
                methods=['GET'], csrf=False)
    @jwt_required
    def students(self, **kw):
        try:
            page = int(kw.get('page', 1))
            size = int(kw.get('size', 20))

            Path = request.env['encoach.adaptive.path'].sudo()
            Event = request.env['encoach.adaptive.event'].sudo()
            paths, total = _paginate(Path, [], page, size, order='id desc')

            items = []
            for p in paths:
                last_signal = Event.search(
                    [('student_id', '=', p.student_id.id), ('event_type', '=', 'signal')],
                    limit=1, order='created_at desc',
                )

                module_queue = []
                try:
                    module_queue = json.loads(p.module_queue or '[]')
                except (json.JSONDecodeError, TypeError):
                    pass

                total_modules = len(module_queue) if module_queue else 1
                completed = sum(
                    1 for m in module_queue
                    if isinstance(m, dict) and m.get('done')
                )
                progress_pct = round(completed / total_modules * 100, 1) if total_modules else 0.0

                current_module = ''
                if module_queue and completed < len(module_queue):
                    entry = module_queue[completed]
                    if isinstance(entry, dict):
                        current_module = entry.get('name', '')
                    elif isinstance(entry, str):
                        current_module = entry

                items.append({
                    'student_id': p.student_id.id,
                    'name': p.student_id.name or '',
                    'course': p.course_id.name if p.course_id else '',
                    'current_module': current_module,
                    'progress_pct': progress_pct,
                    'last_signal': {
                        'signal_name': last_signal.signal_name or '',
                        'signal_value': last_signal.signal_value,
                        'created_at': last_signal.created_at,
                    } if last_signal else None,
                })

            return _json_response({
                'items': items,
                'total': total,
                'page': page,
                'size': size,
            })

        except Exception as e:
            _logger.exception('adaptive students list failed')
            return _error_response(str(e), 500)

    # ------------------------------------------------------------------
    # GET /api/adaptive/student/<int:student_id>/signals
    # ------------------------------------------------------------------
    @http.route('/api/adaptive/student/<int:student_id>/signals', type='http',
                auth='none', methods=['GET'], csrf=False)
    @jwt_required
    def student_signals(self, student_id, **kw):
        try:
            page = int(kw.get('page', 1))
            size = int(kw.get('size', 20))

            Event = request.env['encoach.adaptive.event'].sudo()
            domain = [('student_id', '=', student_id)]
            events, total = _paginate(Event, domain, page, size, order='created_at desc')

            items = []
            for ev in events:
                items.append({
                    'id': ev.id,
                    'event_type': ev.event_type,
                    'signal_name': ev.signal_name or '',
                    'signal_value': ev.signal_value,
                    'decision': ev.decision or '',
                    'created_at': ev.created_at,
                })

            return _json_response({
                'items': items,
                'total': total,
                'page': page,
                'size': size,
            })

        except Exception as e:
            _logger.exception('student signals failed')
            return _error_response(str(e), 500)

    # ------------------------------------------------------------------
    # GET /api/adaptive/student/<int:student_id>/ability
    # ------------------------------------------------------------------
    @http.route('/api/adaptive/student/<int:student_id>/ability', type='http',
                auth='none', methods=['GET'], csrf=False)
    @jwt_required
    def student_ability(self, student_id, **kw):
        try:
            Event = request.env['encoach.adaptive.event'].sudo()
            signals = Event.search([
                ('student_id', '=', student_id),
                ('event_type', '=', 'signal'),
            ], order='created_at asc')

            trajectory = []
            for s in signals:
                trajectory.append({
                    'signal_name': s.signal_name or '',
                    'value': s.signal_value,
                    'timestamp': s.created_at,
                })

            values = [s.signal_value for s in signals if s.signal_value]
            theta = sum(values) / len(values) if values else 0.0
            sem = math.sqrt(sum((v - theta) ** 2 for v in values) / len(values)) if len(values) > 1 else 1.0

            return _json_response({
                'student_id': student_id,
                'theta': round(theta, 3),
                'sem': round(sem, 3),
                'trajectory': trajectory,
                'n_signals': len(trajectory),
            })

        except Exception as e:
            _logger.exception('student ability failed')
            return _error_response(str(e), 500)

    # ------------------------------------------------------------------
    # GET /api/adaptive/student/<int:student_id>/recommended-resources
    # ------------------------------------------------------------------
    @http.route('/api/adaptive/student/<int:student_id>/recommended-resources',
                type='http', auth='none', methods=['GET'], csrf=False)
    @jwt_required
    def recommended_resources(self, student_id, **kw):
        try:
            profile = request.env['encoach.student.profile'].sudo().search(
                [('user_id', '=', student_id)], limit=1)
            learning_style = profile.learning_style if profile else ''

            # Get resources for student's current course/module
            path = request.env['encoach.adaptive.path'].sudo().search(
                [('student_id', '=', student_id)], limit=1, order='id desc')

            resources = request.env['encoach.resource'].sudo().search(
                [('active', '=', True), ('review_status', '=', 'approved')], limit=50)

            if learning_style:
                from odoo.addons.encoach_adaptive.services.style_matcher import StyleMatcher
                ranked = StyleMatcher.rank_resources(learning_style, resources)
            else:
                ranked = list(resources)

            items = []
            for r in ranked[:20]:
                items.append({
                    'id': r.id,
                    'name': r.name,
                    'type': r.type or '',
                    'cefr_level': r.cefr_level or '',
                    'difficulty': r.difficulty or '',
                    'duration_minutes': r.duration_minutes,
                    'style_match': learning_style if r.type in (
                        STYLE_RESOURCE_MAP.get(learning_style, []) if learning_style else []
                    ) else '',
                })

            return _json_response({
                'student_id': student_id,
                'learning_style': learning_style or 'none',
                'items': items,
            })
        except Exception as e:
            _logger.exception('recommended_resources failed')
            return _error_response(str(e), 500)

    # ------------------------------------------------------------------
    # GET /api/adaptive/settings
    # ------------------------------------------------------------------
    @http.route('/api/adaptive/settings', type='http', auth='none',
                methods=['GET'], csrf=False)
    @jwt_required
    def get_settings(self, **kw):
        try:
            user = request.env.user.sudo()
            Settings = request.env['encoach.adaptive.settings'].sudo()
            settings = Settings.search([('teacher_id', '=', user.id)], limit=1)

            if not settings:
                return _json_response({
                    'step_up_threshold': 0.85,
                    'step_down_threshold': 0.50,
                    'micro_lesson_trigger': 2,
                    'module_skip_threshold': 0.95,
                    'no_progress_alert_days': 3,
                    'max_retries': 3,
                    'is_default': True,
                })

            return _json_response({
                'id': settings.id,
                'step_up_threshold': settings.step_up_threshold,
                'step_down_threshold': settings.step_down_threshold,
                'micro_lesson_trigger': settings.micro_lesson_trigger,
                'module_skip_threshold': settings.module_skip_threshold,
                'no_progress_alert_days': settings.no_progress_alert_days,
                'max_retries': settings.max_retries,
                'is_default': False,
            })

        except Exception as e:
            _logger.exception('get adaptive settings failed')
            return _error_response(str(e), 500)

    # ------------------------------------------------------------------
    # PUT /api/adaptive/settings
    # ------------------------------------------------------------------
    @http.route('/api/adaptive/settings', type='http', auth='none',
                methods=['PUT'], csrf=False)
    @jwt_required
    def update_settings(self, **kw):
        try:
            body = _get_json_body()
            user = request.env.user.sudo()
            Settings = request.env['encoach.adaptive.settings'].sudo()
            settings = Settings.search([('teacher_id', '=', user.id)], limit=1)

            allowed_fields = [
                'step_up_threshold', 'step_down_threshold', 'micro_lesson_trigger',
                'module_skip_threshold', 'no_progress_alert_days', 'max_retries',
            ]
            vals = {k: body[k] for k in allowed_fields if k in body}

            if not settings:
                vals['teacher_id'] = user.id
                entity = user.entity_ids[:1]
                if entity:
                    vals['entity_id'] = entity.id
                settings = Settings.create(vals)
            else:
                settings.write(vals)

            return _json_response({
                'id': settings.id,
                'step_up_threshold': settings.step_up_threshold,
                'step_down_threshold': settings.step_down_threshold,
                'micro_lesson_trigger': settings.micro_lesson_trigger,
                'module_skip_threshold': settings.module_skip_threshold,
                'no_progress_alert_days': settings.no_progress_alert_days,
                'max_retries': settings.max_retries,
            })

        except Exception as e:
            _logger.exception('update adaptive settings failed')
            return _error_response(str(e), 500)
