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

_logger = logging.getLogger(__name__)


def _ser_chapter(c):
    objectives = getattr(c, 'learning_objective_ids', c.env['encoach.learning.objective'])
    return {
        'id': c.id,
        'name': c.name or '',
        'course_id': c.course_id.id if c.course_id else 0,
        'course_name': c.course_id.name if c.course_id else '',
        'sequence': c.sequence,
        'description': c.description or '',
        'start_date': str(c.start_date) if c.start_date else None,
        'end_date': str(c.end_date) if c.end_date else None,
        'unlock_mode': c.unlock_mode or 'manual',
        'is_unlocked': c.is_unlocked,
        'topic_id': c.topic_id.id if c.topic_id else None,
        'topic_name': c.topic_id.name if c.topic_id else None,
        'domain_id': c.domain_id.id if c.domain_id else None,
        'domain_name': c.domain_id.name if c.domain_id else None,
        'learning_objective_ids': objectives.ids,
        'learning_objective_names': objectives.mapped('name'),
        'material_count': c.material_count or 0,
        'assignment_ids': [],
        'exam_ids': [],
    }


def _ser_material(m):
    return {
        'id': m.id,
        'name': m.name or '',
        'chapter_id': m.chapter_id.id,
        'type': m.type or 'pdf',
        'file_url': f'/api/materials/{m.id}/download' if m.file_data else None,
        'url': m.url or None,
        'description': m.description or None,
        'sequence': m.sequence,
        'allow_download': m.allow_download,
        'is_book': m.is_book,
        'book_chapters': m.book_chapters or None,
    }


class CoursewareController(http.Controller):

    # ── Global Material Search ────────────────────────────────────────

    @http.route('/api/materials/search', type='http', auth='public', methods=['GET'], csrf=False)
    @jwt_required
    def search_materials(self, **kw):
        """Search all materials across all courses. Used by Admin Resources page."""
        try:
            M = request.env['encoach.chapter.material'].sudo()
            domain = []
            if kw.get('search'):
                domain.append(('name', 'ilike', kw['search']))
            if kw.get('type') and kw['type'] != 'all':
                domain.append(('type', '=', kw['type']))
            if kw.get('course_id'):
                chapters = request.env['encoach.course.chapter'].sudo().search([
                    ('course_id', '=', int(kw['course_id']))
                ])
                domain.append(('chapter_id', 'in', chapters.ids))
            total = M.search_count(domain)
            limit = min(int(kw.get('limit', 50)), 200)
            offset = int(kw.get('offset', 0))
            recs = M.search(domain, limit=limit, offset=offset, order='id desc')
            items = []
            for m in recs:
                d = _ser_material(m)
                d['course_id'] = m.chapter_id.course_id.id if m.chapter_id.course_id else 0
                d['course_name'] = m.chapter_id.course_id.name if m.chapter_id.course_id else ''
                d['chapter_name'] = m.chapter_id.name if m.chapter_id else ''
                d['source'] = 'course_material'
                items.append(d)
            return _json_response({'items': items, 'total': total})
        except Exception as e:
            return _error_response(str(e), 500)

    # ── Chapters ─────────────────────────────────────────────────────

    @http.route('/api/courses/<int:course_id>/chapters', type='http', auth='public', methods=['GET'], csrf=False)
    @jwt_required
    def list_chapters(self, course_id, **kw):
        try:
            M = request.env['encoach.course.chapter'].sudo()
            recs = M.search([('course_id', '=', course_id)], order='sequence, id')
            return _json_response([_ser_chapter(r) for r in recs])
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/courses/<int:course_id>/chapters', type='http', auth='public', methods=['POST'], csrf=False)
    @jwt_required
    def create_chapter(self, course_id, **kw):
        try:
            body = _get_json_body()
            vals = {
                'name': body.get('name', ''),
                'course_id': course_id,
                'sequence': body.get('sequence', 10),
                'description': body.get('description', ''),
                'unlock_mode': body.get('unlock_mode', 'manual'),
            }
            if body.get('start_date'):
                vals['start_date'] = body['start_date']
            if body.get('end_date'):
                vals['end_date'] = body['end_date']
            if body.get('topic_id'):
                vals['topic_id'] = int(body['topic_id'])
            if body.get('learning_objective_ids'):
                vals['learning_objective_ids'] = [(6, 0, [int(i) for i in body['learning_objective_ids']])]
            rec = request.env['encoach.course.chapter'].sudo().create(vals)
            return _json_response(_ser_chapter(rec))
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>', type='http', auth='public', methods=['GET'], csrf=False)
    @jwt_required
    def get_chapter(self, cid, **kw):
        try:
            rec = request.env['encoach.course.chapter'].sudo().browse(cid)
            if not rec.exists():
                return _error_response('Not found', 404)
            return _json_response(_ser_chapter(rec))
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>', type='http', auth='public', methods=['PATCH', 'PUT'], csrf=False)
    @jwt_required
    def update_chapter(self, cid, **kw):
        try:
            rec = request.env['encoach.course.chapter'].sudo().browse(cid)
            if not rec.exists():
                return _error_response('Not found', 404)
            body = _get_json_body()
            vals = {}
            for k in ('name', 'description', 'unlock_mode', 'start_date', 'end_date'):
                if k in body:
                    vals[k] = body[k]
            if 'sequence' in body:
                vals['sequence'] = int(body['sequence'])
            if 'topic_id' in body:
                vals['topic_id'] = int(body['topic_id']) if body['topic_id'] else False
            if 'is_unlocked' in body:
                vals['is_unlocked'] = bool(body['is_unlocked'])
            if 'learning_objective_ids' in body:
                vals['learning_objective_ids'] = [(6, 0, [int(i) for i in body['learning_objective_ids']])]
            if vals:
                rec.write(vals)
            return _json_response(_ser_chapter(rec))
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>', type='http', auth='public', methods=['DELETE'], csrf=False)
    @jwt_required
    def delete_chapter(self, cid, **kw):
        try:
            rec = request.env['encoach.course.chapter'].sudo().browse(cid)
            if rec.exists():
                rec.unlink()
            return _json_response({'success': True})
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>/unlock', type='http', auth='public', methods=['POST'], csrf=False)
    @jwt_required
    def unlock_chapter(self, cid, **kw):
        try:
            rec = request.env['encoach.course.chapter'].sudo().browse(cid)
            if not rec.exists():
                return _error_response('Not found', 404)
            rec.write({'is_unlocked': True})
            return _json_response({'success': True})
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>/lock', type='http', auth='public', methods=['POST'], csrf=False)
    @jwt_required
    def lock_chapter(self, cid, **kw):
        try:
            rec = request.env['encoach.course.chapter'].sudo().browse(cid)
            if not rec.exists():
                return _error_response('Not found', 404)
            rec.write({'is_unlocked': False})
            return _json_response({'success': True})
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/courses/<int:course_id>/chapters/reorder', type='http', auth='public', methods=['PATCH', 'PUT'], csrf=False)
    @jwt_required
    def reorder_chapters(self, course_id, **kw):
        try:
            body = _get_json_body()
            ids = body.get('ids', [])
            for seq, cid in enumerate(ids):
                rec = request.env['encoach.course.chapter'].sudo().browse(int(cid))
                if rec.exists():
                    rec.write({'sequence': seq * 10})
            return _json_response({'success': True})
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>/progress', type='http', auth='public', methods=['GET'], csrf=False)
    @jwt_required
    def get_chapter_progress(self, cid, **kw):
        try:
            user = request.env['res.users'].sudo().browse(request.env.uid)
            student = request.env['op.student'].sudo().search([('user_id', '=', user.id)], limit=1)
            if student:
                prog = request.env['encoach.chapter.progress'].sudo().search([
                    ('chapter_id', '=', cid), ('student_id', '=', student.id)
                ], limit=1)
                if prog:
                    return _json_response({
                        'id': prog.id,
                        'student_id': student.id,
                        'student_name': student.partner_id.name or '',
                        'chapter_id': cid,
                        'status': prog.status,
                        'started_at': str(prog.started_at) if prog.started_at else None,
                        'completed_at': str(prog.completed_at) if prog.completed_at else None,
                        'materials_completed': prog.materials_completed,
                        'materials_total': prog.materials_total,
                    })
            return _json_response({
                'id': 0, 'student_id': 0, 'student_name': '', 'chapter_id': cid,
                'status': 'not_started', 'started_at': None, 'completed_at': None,
                'materials_completed': 0, 'materials_total': 0,
            })
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>/progress/complete', type='http', auth='public', methods=['POST'], csrf=False)
    @jwt_required
    def mark_chapter_complete(self, cid, **kw):
        try:
            user = request.env['res.users'].sudo().browse(request.env.uid)
            student = request.env['op.student'].sudo().search([('user_id', '=', user.id)], limit=1)
            if not student:
                return _error_response('Student not found for current user', 404)
            chapter = request.env['encoach.course.chapter'].sudo().browse(cid)
            if not chapter.exists():
                return _error_response('Chapter not found', 404)

            Progress = request.env['encoach.chapter.progress'].sudo()
            prog = Progress.search([
                ('chapter_id', '=', cid),
                ('student_id', '=', student.id),
            ], limit=1)
            now = fields.Datetime.now()
            if prog:
                prog.write({
                    'status': 'completed',
                    'completed_at': now,
                    'materials_completed': chapter.material_count,
                    'materials_total': chapter.material_count,
                })
            else:
                prog = Progress.create({
                    'chapter_id': cid,
                    'student_id': student.id,
                    'status': 'completed',
                    'started_at': now,
                    'completed_at': now,
                    'materials_completed': chapter.material_count,
                    'materials_total': chapter.material_count,
                })

            course_id = chapter.course_id.id
            self._check_course_completion(student.id, course_id)

            return _json_response({
                'success': True,
                'chapter_id': cid,
                'status': 'completed',
            })
        except Exception as e:
            _logger.exception('mark_chapter_complete failed')
            return _error_response(str(e), 500)

    def _check_course_completion(self, student_id, course_id):
        """Check if all chapters are completed; if so, mark course as completed and enable post-test."""
        chapters = request.env['encoach.course.chapter'].sudo().search([
            ('course_id', '=', course_id)
        ])
        if not chapters:
            return
        completed = request.env['encoach.chapter.progress'].sudo().search_count([
            ('student_id', '=', student_id),
            ('chapter_id', 'in', chapters.ids),
            ('status', '=', 'completed'),
        ])
        total = len(chapters)
        progress_pct = int((completed / total * 100) if total else 0)
        Completion = request.env['encoach.course.completion'].sudo()
        comp = Completion.search([
            ('student_id', '=', student_id),
            ('course_id', '=', course_id),
        ], limit=1)
        vals = {
            'chapters_total': total,
            'chapters_completed': completed,
            'progress_percent': progress_pct,
        }
        if completed >= total:
            vals['status'] = 'completed'
            vals['completed_at'] = fields.Datetime.now()
            vals['post_test_available'] = True
        else:
            vals['status'] = 'in_progress'
            vals['post_test_available'] = False
        if comp:
            comp.write(vals)
        else:
            vals['student_id'] = student_id
            vals['course_id'] = course_id
            Completion.create(vals)

    @http.route('/api/courses/<int:course_id>/completion', type='http', auth='public', methods=['GET'], csrf=False)
    @jwt_required
    def get_course_completion(self, course_id, **kw):
        """Get course completion status for the current student."""
        try:
            user = request.env['res.users'].sudo().browse(request.env.uid)
            student = request.env['op.student'].sudo().search([('user_id', '=', user.id)], limit=1)
            if not student:
                return _json_response({
                    'course_id': course_id,
                    'status': 'not_enrolled',
                    'chapters_total': 0,
                    'chapters_completed': 0,
                    'progress_percent': 0,
                    'post_test_available': False,
                })
            comp = request.env['encoach.course.completion'].sudo().search([
                ('student_id', '=', student.id),
                ('course_id', '=', course_id),
            ], limit=1)
            if comp:
                return _json_response({
                    'course_id': course_id,
                    'status': comp.status,
                    'chapters_total': comp.chapters_total,
                    'chapters_completed': comp.chapters_completed,
                    'progress_percent': comp.progress_percent,
                    'completed_at': str(comp.completed_at) if comp.completed_at else None,
                    'post_test_available': comp.post_test_available,
                })
            chapters = request.env['encoach.course.chapter'].sudo().search([
                ('course_id', '=', course_id)
            ])
            completed = request.env['encoach.chapter.progress'].sudo().search_count([
                ('student_id', '=', student.id),
                ('chapter_id', 'in', chapters.ids),
                ('status', '=', 'completed'),
            ])
            total = len(chapters)
            return _json_response({
                'course_id': course_id,
                'status': 'in_progress' if completed > 0 else 'not_started',
                'chapters_total': total,
                'chapters_completed': completed,
                'progress_percent': int((completed / total * 100) if total else 0),
                'post_test_available': completed >= total and total > 0,
            })
        except Exception as e:
            _logger.exception('get_course_completion failed')
            return _error_response(str(e), 500)

    # ── Materials ────────────────────────────────────────────────────

    @http.route('/api/chapters/<int:cid>/materials', type='http', auth='public', methods=['GET'], csrf=False)
    @jwt_required
    def list_materials(self, cid, **kw):
        try:
            M = request.env['encoach.chapter.material'].sudo()
            recs = M.search([('chapter_id', '=', cid)], order='sequence, id')
            return _json_response([_ser_material(r) for r in recs])
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>/materials', type='http', auth='public', methods=['POST'], csrf=False)
    @jwt_required
    def upload_material(self, cid, **kw):
        try:
            files = request.httprequest.files
            f = files.get('file')
            body = {}
            ct = request.httprequest.content_type or ''
            if 'application/json' in ct:
                body = _get_json_body()
            params = {**body, **kw}
            vals = {
                'chapter_id': cid,
                'name': params.get('name', f.filename if f else 'Material'),
                'type': params.get('type', 'pdf'),
                'sequence': int(params.get('sequence', 10)),
                'allow_download': str(params.get('allow_download', 'true')).lower() == 'true',
            }
            if f:
                vals['file_data'] = base64.b64encode(f.read())
                vals['file_name'] = f.filename
            if params.get('url'):
                vals['url'] = params['url']
            if params.get('description'):
                vals['description'] = params['description']
            rec = request.env['encoach.chapter.material'].sudo().create(vals)
            return _json_response(_ser_material(rec))
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>/materials/from-resource', type='http', auth='public', methods=['POST'], csrf=False)
    @jwt_required
    def create_material_from_resource(self, cid, **kw):
        """Create a chapter material by copying from an existing library resource."""
        try:
            body = _get_json_body()
            resource_id = body.get('resource_id')
            if not resource_id:
                return _error_response('resource_id is required', 400)
            res = request.env['encoach.resource'].sudo().browse(int(resource_id))
            if not res.exists():
                return _error_response('Resource not found', 404)
            type_map = {
                'pdf': 'pdf', 'document': 'document', 'video': 'video',
                'link': 'link', 'interactive': 'document',
            }
            vals = {
                'chapter_id': cid,
                'name': body.get('name') or res.name,
                'type': type_map.get(res.type, 'document'),
                'sequence': int(body.get('sequence', 10)),
                'allow_download': True,
                'url': res.url or '',
                'resource_id': res.id,
            }
            if res.file:
                vals['file_data'] = res.file
                vals['file_name'] = res.name
            rec = request.env['encoach.chapter.material'].sudo().create(vals)
            return _json_response(_ser_material(rec))
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/materials/<int:mid>', type='http', auth='public', methods=['PATCH', 'PUT'], csrf=False)
    @jwt_required
    def update_material(self, mid, **kw):
        try:
            rec = request.env['encoach.chapter.material'].sudo().browse(mid)
            if not rec.exists():
                return _error_response('Not found', 404)
            body = _get_json_body()
            vals = {}
            for k in ('name', 'type', 'url', 'description'):
                if k in body:
                    vals[k] = body[k]
            if 'sequence' in body:
                vals['sequence'] = int(body['sequence'])
            if 'allow_download' in body:
                vals['allow_download'] = bool(body['allow_download'])
            if vals:
                rec.write(vals)
            return _json_response(_ser_material(rec))
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/materials/<int:mid>', type='http', auth='public', methods=['DELETE'], csrf=False)
    @jwt_required
    def delete_material(self, mid, **kw):
        try:
            rec = request.env['encoach.chapter.material'].sudo().browse(mid)
            if rec.exists():
                rec.unlink()
            return _json_response({'success': True})
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/materials/<int:mid>/download', type='http', auth='public', methods=['GET'], csrf=False)
    @jwt_required
    def download_material(self, mid, **kw):
        try:
            rec = request.env['encoach.chapter.material'].sudo().browse(mid)
            if not rec.exists() or not rec.file_data:
                return _error_response('Not found', 404)
            data = base64.b64decode(rec.file_data)
            fname = rec.file_name or 'download'
            return request.make_response(data, headers=[
                ('Content-Type', 'application/octet-stream'),
                ('Content-Disposition', f'attachment; filename="{fname}"'),
            ])
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/materials/<int:mid>/content', type='http', auth='public', methods=['GET'], csrf=False)
    def material_content(self, mid, **kw):
        """Serve material file inline (for iframe/embed) instead of as download.
        Accepts JWT via Authorization header OR ?token= query param (for iframe src)."""
        try:
            from odoo.addons.encoach_api.controllers.base import validate_token, _get_jwt_secret
            import jwt as pyjwt
            user = validate_token()
            if not user:
                token_param = kw.get('token') or request.httprequest.args.get('token')
                if token_param:
                    secret = _get_jwt_secret()
                    if secret:
                        try:
                            payload = pyjwt.decode(token_param, secret, algorithms=["HS256"])
                            uid = payload.get("uid")
                            if uid:
                                user = request.env['res.users'].sudo().browse(int(uid))
                                if user.exists():
                                    request.update_env(user=user.id)
                        except Exception:
                            pass
            if not user:
                return _error_response("Authentication required", 401)
            rec = request.env['encoach.chapter.material'].sudo().browse(mid)
            if not rec.exists() or not rec.file_data:
                return _error_response('Not found', 404)
            data = base64.b64decode(rec.file_data)
            fname = rec.file_name or 'file'
            ext = fname.rsplit('.', 1)[-1].lower() if '.' in fname else ''
            mime_map = {
                'pdf': 'application/pdf',
                'mp4': 'video/mp4', 'webm': 'video/webm', 'ogg': 'video/ogg',
                'mp3': 'audio/mpeg', 'wav': 'audio/wav',
                'png': 'image/png', 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg',
                'gif': 'image/gif', 'svg': 'image/svg+xml', 'webp': 'image/webp',
                'html': 'text/html', 'htm': 'text/html',
                'txt': 'text/plain',
            }
            content_type = mime_map.get(ext, 'application/octet-stream')
            return request.make_response(data, headers=[
                ('Content-Type', content_type),
                ('Content-Disposition', f'inline; filename="{fname}"'),
                ('Cache-Control', 'public, max-age=3600'),
            ])
        except Exception as e:
            return _error_response(str(e), 500)

    @http.route('/api/materials/<int:mid>/viewed', type='http', auth='public', methods=['POST'], csrf=False)
    @jwt_required
    def mark_viewed(self, mid, **kw):
        """Mark a material as viewed and update chapter progress."""
        try:
            material = request.env['encoach.chapter.material'].sudo().browse(mid)
            if not material.exists():
                return _error_response('Not found', 404)
            user = request.env['res.users'].sudo().browse(request.env.uid)
            student = request.env['op.student'].sudo().search([('user_id', '=', user.id)], limit=1)
            if not student:
                return _json_response({'success': True})
            chapter = material.chapter_id
            Progress = request.env['encoach.chapter.progress'].sudo()
            prog = Progress.search([
                ('chapter_id', '=', chapter.id),
                ('student_id', '=', student.id),
            ], limit=1)
            now = fields.Datetime.now()
            if prog:
                new_count = min(prog.materials_completed + 1, chapter.material_count)
                vals = {
                    'materials_completed': new_count,
                    'materials_total': chapter.material_count,
                }
                if prog.status == 'not_started':
                    vals['status'] = 'in_progress'
                    vals['started_at'] = now
                if new_count >= chapter.material_count:
                    vals['status'] = 'completed'
                    vals['completed_at'] = now
                prog.write(vals)
            else:
                status = 'completed' if chapter.material_count <= 1 else 'in_progress'
                prog = Progress.create({
                    'chapter_id': chapter.id,
                    'student_id': student.id,
                    'status': status,
                    'started_at': now,
                    'completed_at': now if status == 'completed' else False,
                    'materials_completed': 1,
                    'materials_total': chapter.material_count,
                })
            if prog.status == 'completed':
                self._check_course_completion(student.id, chapter.course_id.id)
            return _json_response({'success': True, 'materials_completed': prog.materials_completed})
        except Exception as e:
            _logger.exception('mark_viewed failed')
            return _error_response(str(e), 500)

    @http.route('/api/chapters/<int:cid>/materials/reorder', type='http', auth='public', methods=['PATCH', 'PUT'], csrf=False)
    @jwt_required
    def reorder_materials(self, cid, **kw):
        try:
            body = _get_json_body()
            ids = body.get('ids', [])
            for seq, mid in enumerate(ids):
                rec = request.env['encoach.chapter.material'].sudo().browse(int(mid))
                if rec.exists():
                    rec.write({'sequence': seq * 10})
            return _json_response({'success': True})
        except Exception as e:
            return _error_response(str(e), 500)
