import json
import hashlib
import logging
import random
import string
from datetime import timedelta

import requests as http_requests

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__)


class EncoachSignupController(http.Controller):

    # ------------------------------------------------------------------
    # POST /api/auth/register
    # ------------------------------------------------------------------
    @http.route('/api/auth/register', type='http', auth='public',
                methods=['POST'], csrf=False)
    def register(self, **kw):
        try:
            body = _get_json_body()
            email = (body.get('email') or '').strip().lower()
            password = body.get('password')
            name = body.get('name', '').strip()
            role = body.get('role', 'student')
            captcha_token = body.get('captcha_token')

            if not email or not password or not name:
                return _error_response('email, password, and name are required', 400)

            valid_roles = ('academic_student', 'self_learner', 'teacher', 'student')
            if role not in valid_roles:
                return _error_response(f'role must be one of: {", ".join(valid_roles)}', 400)

            # Map frontend role names to user_type values
            role_to_user_type = {
                'academic_student': 'student',
                'self_learner': 'student',
                'teacher': 'teacher',
                'student': 'student',
            }

            ICP = request.env['ir.config_parameter'].sudo()
            secret_key = ICP.get_param('encoach.captcha_secret_key', '')
            provider = ICP.get_param('encoach.captcha_provider', 'recaptcha')

            if captcha_token and secret_key:
                verify_urls = {
                    'recaptcha': 'https://www.google.com/recaptcha/api/siteverify',
                    'hcaptcha': 'https://hcaptcha.com/siteverify',
                    'turnstile': 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
                }
                verify_url = verify_urls.get(provider)
                if verify_url:
                    try:
                        resp = http_requests.post(verify_url, data={
                            'secret': secret_key,
                            'response': captcha_token,
                        }, timeout=10)
                        if not resp.json().get('success'):
                            return _error_response('CAPTCHA verification failed', 400)
                    except Exception:
                        return _error_response('CAPTCHA verification failed', 400)

            existing = request.env['res.users'].sudo().search(
                [('login', '=', email)], limit=1)
            if existing:
                return _error_response('Email already registered', 409)

            company = request.env['res.company'].sudo().search([], limit=1)
            user = request.env['res.users'].sudo().with_context(
                no_reset_password=True,
            ).create({
                'login': email,
                'email': email,
                'name': name,
                'password': password,
                'company_id': company.id,
                'company_ids': [(4, company.id)],
                'user_type': role_to_user_type.get(role, 'student'),
                'account_source': 'self_registered',
                'account_status': 'unactivated',
            })

            otp_code = ''.join(random.choices(string.digits, k=6))
            otp_hash = hashlib.sha256(otp_code.encode()).hexdigest()
            expires_at = fields.Datetime.now() + timedelta(minutes=15)

            request.env['encoach.otp'].sudo().create({
                'email': email,
                'otp_hash': otp_hash,
                'expires_at': expires_at,
            })

            _logger.info('Registration OTP for %s: %s', email, otp_code)

            return _json_response({
                'message': 'Registration successful. Please verify your email.',
                'user_id': user.id,
                'email': email,
                'role': role,
            }, 201)

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

    # ------------------------------------------------------------------
    # POST /api/auth/check-email
    # ------------------------------------------------------------------
    @http.route('/api/auth/check-email', type='http', auth='none',
                methods=['POST'], csrf=False)
    def check_email(self, **kw):
        try:
            body = _get_json_body()
            email = (body.get('email') or '').strip().lower()
            if not email:
                return _error_response('email is required', 400)

            exists = bool(request.env['res.users'].sudo().search(
                [('login', '=', email)], limit=1))
            return _json_response({'exists': exists})

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

    # ------------------------------------------------------------------
    # POST /api/auth/verify-email
    # ------------------------------------------------------------------
    @http.route('/api/auth/verify-email', type='http', auth='none',
                methods=['POST'], csrf=False)
    def verify_email(self, **kw):
        try:
            body = _get_json_body()
            email = (body.get('email') or '').strip().lower()
            otp = body.get('otp', '').strip()

            if not email or not otp:
                return _error_response('email and otp are required', 400)

            otp_hash = hashlib.sha256(otp.encode()).hexdigest()
            now = fields.Datetime.now()

            record = request.env['encoach.otp'].sudo().search([
                ('email', '=', email),
                ('otp_hash', '=', otp_hash),
                ('used', '=', False),
                ('expires_at', '>=', now),
            ], limit=1, order='created_at desc')

            if not record:
                return _error_response('Invalid or expired OTP', 400)

            record.write({'used': True})

            user = request.env['res.users'].sudo().search(
                [('login', '=', email)], limit=1)
            if user:
                user.write({'is_verified': True})

            return _json_response({'verified': True})

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

    # ------------------------------------------------------------------
    # POST /api/auth/resend-otp
    # ------------------------------------------------------------------
    @http.route('/api/auth/resend-otp', type='http', auth='none',
                methods=['POST'], csrf=False)
    def resend_otp(self, **kw):
        try:
            body = _get_json_body()
            email = (body.get('email') or '').strip().lower()
            if not email:
                return _error_response('email is required', 400)

            latest = request.env['encoach.otp'].sudo().search([
                ('email', '=', email),
            ], limit=1, order='created_at desc')

            if latest and latest.resend_count >= 3:
                return _error_response('Maximum resend attempts reached', 429)

            otp_code = ''.join(random.choices(string.digits, k=6))
            otp_hash = hashlib.sha256(otp_code.encode()).hexdigest()
            expires_at = fields.Datetime.now() + timedelta(minutes=15)
            new_count = (latest.resend_count + 1) if latest else 1

            request.env['encoach.otp'].sudo().create({
                'email': email,
                'otp_hash': otp_hash,
                'expires_at': expires_at,
                'resend_count': new_count,
            })

            _logger.info('Resend OTP for %s: %s', email, otp_code)

            return _json_response({'message': 'OTP resent'})

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

    # ------------------------------------------------------------------
    # GET /api/onboarding/goals
    # ------------------------------------------------------------------
    @http.route('/api/onboarding/goals', type='http', auth='none',
                methods=['GET'], csrf=False)
    @jwt_required
    def onboarding_goals(self, **kw):
        try:
            # Dynamic goals from exam templates in DB
            Template = request.env['encoach.exam.template'].sudo()
            templates = Template.search([('active', '=', True)])
            seen = set()
            goals = []
            icon_map = {
                'ielts': 'fa-language',
                'general_english': 'fa-comments',
                'general english': 'fa-comments',
                'academic': 'fa-graduation-cap',
                'math': 'fa-calculator',
                'mathematics': 'fa-calculator',
                'it': 'fa-laptop',
                'information technology': 'fa-laptop',
            }
            for tpl in templates:
                subj = tpl.subject_id
                if subj and subj.name and subj.name.lower() not in seen:
                    seen.add(subj.name.lower())
                    goal_id = subj.name.lower().replace(' ', '_')
                    goals.append({
                        'id': goal_id,
                        'name': subj.name,
                        'icon': icon_map.get(goal_id, icon_map.get(subj.name.lower(), 'fa-book')),
                    })
            # Fallback if no templates found
            if not goals:
                goals = [
                    {'id': 'ielts', 'name': 'IELTS Preparation', 'icon': 'fa-language'},
                    {'id': 'general_english', 'name': 'General English', 'icon': 'fa-comments'},
                    {'id': 'math', 'name': 'Mathematics', 'icon': 'fa-calculator'},
                    {'id': 'it', 'name': 'Information Technology', 'icon': 'fa-laptop'},
                ]
            return _json_response({'goals': goals})

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

    # ------------------------------------------------------------------
    # POST /api/onboarding/complete
    # ------------------------------------------------------------------
    @http.route('/api/onboarding/complete', type='http', auth='none',
                methods=['POST'], csrf=False)
    @jwt_required
    def onboarding_complete(self, **kw):
        try:
            body = _get_json_body()
            user = request.env.user

            learning_goal = body.get('learning_goal')
            if not learning_goal:
                return _error_response('learning_goal is required', 400)

            skip_placement = body.get('skip_placement', False)
            placement_decision = 'skip' if skip_placement else 'take_now'

            # learning_style: accept as JSON array ["visual","audio"] or string
            learning_style_raw = body.get('learning_style')
            if isinstance(learning_style_raw, list):
                valid_styles = {'visual', 'audio', 'reading', 'mixed'}
                learning_style_raw = [s for s in learning_style_raw if s in valid_styles]
                learning_style_json = json.dumps(learning_style_raw) if learning_style_raw else None
            elif isinstance(learning_style_raw, str):
                learning_style_json = json.dumps([learning_style_raw])
            else:
                learning_style_json = None

            profile_vals = {
                'user_id': user.id,
                'learning_goal': learning_goal,
                'target_band': body.get('target_band', 0.0),
                'hours_per_week': body.get('hours_per_week', 0),
                'study_mode': body.get('study_mode'),
                'exam_date': body.get('exam_date'),
                'placement_decision': placement_decision,
            }

            if learning_style_json:
                profile_vals['learning_style'] = learning_style_json

            # Assign B1 default level when placement test is skipped
            if skip_placement:
                profile_vals['cefr_level'] = 'b1'

            Profile = request.env['encoach.student.profile'].sudo()
            profile = Profile.search([('user_id', '=', user.id)], limit=1)
            if profile:
                profile.write(profile_vals)
            else:
                profile = Profile.create(profile_vals)

            user.sudo().write({
                'account_status': 'activated',
                'first_login': False,
            })

            return _json_response({
                'message': 'Onboarding complete',
                'profile_id': profile.id,
                'placement_decision': placement_decision,
            })

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

    # ------------------------------------------------------------------
    # GET /api/config/captcha
    # ------------------------------------------------------------------
    @http.route('/api/config/captcha', type='http', auth='none',
                methods=['GET'], csrf=False)
    def captcha_config(self, **kw):
        try:
            ICP = request.env['ir.config_parameter'].sudo()
            return _json_response({
                'provider': ICP.get_param('encoach.captcha_provider', 'recaptcha'),
                'site_key': ICP.get_param('encoach.captcha_site_key', ''),
            })

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