# -*- coding: utf-8 -*-

from odoo import http, _
from odoo.http import request
from odoo.addons.portal.controllers.portal import CustomerPortal
import json
import logging

_logger = logging.getLogger(__name__)


class QuotationPortal(CustomerPortal):

    def _prepare_home_portal_values(self, counters):
        values = super()._prepare_home_portal_values(counters)
        if 'wc_assessment_count' in counters:
            Assessment = request.env['fusion.wc.assessment'].sudo()
            values['wc_assessment_count'] = Assessment.search_count([
                ('sales_rep_id', '=', request.env.user.id),
            ])
        return values

    # =========================================================================
    # ASSESSMENT LIST
    # =========================================================================
    @http.route('/my/quotation/builder', type='http', auth='user', website=True)
    def quotation_builder_list(self, **kw):
        Assessment = request.env['fusion.wc.assessment'].sudo()
        assessments = Assessment.search(
            [('sales_rep_id', '=', request.env.user.id)],
            order='create_date desc',
            limit=50,
        )
        # Equipment types with active flows — for "New Assessment" dropdown
        Flow = request.env['fusion.wc.config.flow'].sudo()
        active_flow_etypes = Flow.search([
            ('state', '=', 'active'),
        ]).mapped('equipment_type')
        all_equip_types = request.env['fusion.equipment.type'].sudo().search(
            [('active', '=', True)], order='sequence')
        equipment_types = all_equip_types.filtered(
            lambda et: et.code in active_flow_etypes)

        # Build lookup dict: code → name for template display
        equip_type_map = {et.code: et.name for et in all_equip_types}

        return request.render('fusion_quotations.portal_quotation_list', {
            'assessments': assessments,
            'equipment_types': equipment_types,
            'equip_type_map': equip_type_map,
            'page_name': 'wc_assessments',
        })

    # =========================================================================
    # HELPER: build template context values
    # =========================================================================
    def _get_form_context(self, assessment=None, requested_equipment_type=None):
        """Return common template context for the assessment form.

        Args:
            assessment: existing assessment record, or None for new.
            requested_equipment_type: equipment type code from query param
                (only used when assessment is None — new form).
        """
        from types import SimpleNamespace

        sections = request.env['fusion.wc.section'].sudo().search(
            [('active', '=', True), ('parent_id', '=', False)],
            order='sequence',
        )
        countries = request.env['res.country'].sudo().search([])
        provinces = request.env['res.country.state'].sudo().search(
            [('country_id.code', '=', 'CA')])

        # Equipment types — only show types that have at least one active config flow
        Flow = request.env['fusion.wc.config.flow'].sudo()
        all_equip_types = request.env['fusion.equipment.type'].sudo().search(
            [('active', '=', True)], order='sequence')
        active_flow_etypes = Flow.search([
            ('state', '=', 'active'),
        ]).mapped('equipment_type')
        equipment_types = all_equip_types.filtered(
            lambda et: et.code in active_flow_etypes)

        # ── Flow steps ──
        # Determine the equipment type: assessment field > query param > first available > fallback
        FlowStep = request.env['fusion.wc.config.flow.step'].sudo()
        if assessment:
            equipment_type = assessment.equipment_type
        elif requested_equipment_type and requested_equipment_type in active_flow_etypes:
            equipment_type = requested_equipment_type
        elif active_flow_etypes:
            # Default to first available type with an active flow
            first_type = equipment_types[:1]
            equipment_type = first_type.code if first_type else 'manual_wheelchair'
        else:
            equipment_type = 'manual_wheelchair'

        flow = Flow.search([
            ('equipment_type', '=', equipment_type),
            ('state', '=', 'active'),
        ], limit=1)

        # Auto-create default steps for active flows that have none
        if flow and not flow.step_ids:
            try:
                flow.action_create_default_steps()
            except Exception:
                _logger.warning("Failed to auto-create steps for flow %s", flow.id)

        flow_steps = flow.step_ids.sorted('sequence') if flow else FlowStep.browse()

        # If no flow found, try any active flow as fallback
        if not flow_steps:
            flow = Flow.search([('state', '=', 'active')], limit=1)
            if flow:
                if not flow.step_ids:
                    try:
                        flow.action_create_default_steps()
                    except Exception:
                        pass
                flow_steps = flow.step_ids.sorted('sequence')

        # Final fallback: SimpleNamespace objects matching step record interface
        if not flow_steps:
            flow_steps = [
                SimpleNamespace(id=0, name='Client & Equipment', step_type='client_info',
                    icon='fa-user', section_code='', fields_json='', help_text='', is_required=True),
                SimpleNamespace(id=0, name='Measurements', step_type='measurements',
                    icon='fa-ruler', section_code='', fields_json='', help_text='', is_required=True),
                SimpleNamespace(id=0, name='Frame', step_type='product_select',
                    icon='fa-wheelchair', section_code='frame', fields_json='', help_text='', is_required=True),
                SimpleNamespace(id=0, name='Seating', step_type='options',
                    icon='fa-chair', section_code='seating', fields_json='', help_text='', is_required=True),
                SimpleNamespace(id=0, name='Options', step_type='options',
                    icon='fa-list', section_code='mw_adp_options,mw_accessories', fields_json='', help_text='', is_required=True),
                SimpleNamespace(id=0, name='Review', step_type='review',
                    icon='fa-clipboard', section_code='', fields_json='', help_text='', is_required=True),
            ]

        # Parse fields_json for dynamic measurement/custom steps
        step_fields = {}
        for step in flow_steps:
            fj = step.fields_json if hasattr(step, 'fields_json') else ''
            if fj:
                try:
                    step_fields[step.id] = json.loads(fj)
                except (ValueError, TypeError):
                    step_fields[step.id] = []

        # Pre-build a dict of field values so QWeb can look them up by name
        field_values = {}
        if assessment:
            # Standard wheelchair measurement fields
            for fname in [
                'seat_width', 'seat_depth', 'finished_seat_to_floor_height',
                'back_cane_height', 'finished_back_height',
                'finished_leg_rest_length', 'client_weight',
                'seat_width_unit', 'seat_depth_unit', 'seat_to_floor_unit',
                'cane_height_unit', 'back_height_unit', 'leg_rest_unit',
                'client_weight_unit',
            ]:
                field_values[fname] = assessment[fname] or ''

            # Equipment-specific data from JSON (for non-wheelchair types)
            if assessment.equipment_data_json:
                try:
                    equipment_data = json.loads(assessment.equipment_data_json)
                    if isinstance(equipment_data, dict):
                        field_values.update(equipment_data)
                except (ValueError, TypeError):
                    pass

        return {
            'assessment': assessment,
            'sections': sections,
            'countries': countries,
            'provinces': provinces,
            'field_values': field_values,
            'equipment_types': equipment_types,
            'equipment_type': equipment_type,
            'flow': flow,
            'flow_steps': flow_steps,
            'step_fields': step_fields,
        }

    # =========================================================================
    # NEW ASSESSMENT FORM
    # =========================================================================
    @http.route('/my/quotation/builder/new', type='http', auth='user', website=True)
    def quotation_builder_new(self, **kw):
        ctx = self._get_form_context(
            assessment=None,
            requested_equipment_type=kw.get('equipment_type'),
        )
        ctx['page_name'] = 'wc_assessment_new'
        return request.render('fusion_quotations.portal_quotation_form', ctx)

    # =========================================================================
    # EDIT EXISTING ASSESSMENT
    # =========================================================================
    @http.route('/my/quotation/builder/<int:assessment_id>/edit',
                type='http', auth='user', website=True)
    def quotation_builder_edit(self, assessment_id, **kw):
        Assessment = request.env['fusion.wc.assessment'].sudo()
        assessment = Assessment.browse(assessment_id)

        if not assessment.exists() or assessment.sales_rep_id.id != request.env.user.id:
            return request.redirect('/my/quotation/builder')

        ctx = self._get_form_context(assessment=assessment)
        ctx['page_name'] = 'wc_assessment_edit'
        return request.render('fusion_quotations.portal_quotation_form', ctx)

    # =========================================================================
    # SAVE ASSESSMENT (FINAL SUBMIT)
    # =========================================================================
    @http.route('/my/quotation/builder/save', type='http', auth='user',
                website=True, methods=['POST'], csrf=True)
    def quotation_builder_save(self, **post):
        Assessment = request.env['fusion.wc.assessment'].sudo()
        assessment_id = int(post.get('assessment_id') or 0)

        vals = self._extract_assessment_vals(post)

        if assessment_id:
            assessment = Assessment.browse(assessment_id)
            if assessment.exists() and assessment.sales_rep_id.id == request.env.user.id:
                assessment.write(vals)
        else:
            vals['sales_rep_id'] = request.env.user.id
            assessment = Assessment.create(vals)

        # Process selected items (lines)
        self._process_assessment_lines(assessment, post)

        # Check if user wants to generate quotation
        if post.get('action') == 'generate':
            try:
                assessment.action_generate_quotation()
                return request.redirect(
                    f'/my/quotation/builder/{assessment.id}/edit?success=quotation_generated')
            except Exception as e:
                _logger.error("Quotation generation failed: %s", str(e))
                return request.redirect(
                    f'/my/quotation/builder/{assessment.id}/edit?error={str(e)}')

        return request.redirect(
            f'/my/quotation/builder/{assessment.id}/edit?success=saved')

    # Known POST keys that map to standard assessment fields (not dynamic)
    _KNOWN_POST_KEYS = {
        'csrf_token', 'assessment_id', 'current_step', 'action', 'access_token',
        'line_product_ids', 'line_section_ids', 'line_build_types',
        'line_quantities', 'line_rationales',
        'partner_id', 'create_new_partner',
        'client_first_name', 'client_last_name', 'client_phone', 'client_email',
        'client_street', 'client_street2', 'client_city', 'client_state_id',
        'client_zip', 'client_country_id', 'client_dob', 'client_health_card',
        'equipment_type', 'wheelchair_type', 'powerchair_type',
        'client_type', 'build_type', 'reason_for_application', 'authorizer_id',
        'seat_width', 'seat_depth', 'finished_seat_to_floor_height',
        'back_cane_height', 'finished_back_height', 'finished_leg_rest_length',
        'client_weight',
        'seat_width_unit', 'seat_depth_unit', 'seat_to_floor_unit',
        'cane_height_unit', 'back_height_unit', 'leg_rest_unit',
        'client_weight_unit',
        'frame_product_tmpl_id', 'frame_product_id', 'frame_notes', 'notes',
    }

    def _extract_assessment_vals(self, post):
        """Extract assessment field values from POST data."""
        vals = {}

        # Client
        partner_id = post.get('partner_id')
        if partner_id and partner_id != '0':
            vals['partner_id'] = int(partner_id)
            vals['create_new_partner'] = False
        else:
            vals['create_new_partner'] = True
            vals['client_first_name'] = post.get('client_first_name', '')
            vals['client_last_name'] = post.get('client_last_name', '')
            vals['client_phone'] = post.get('client_phone', '')
            vals['client_email'] = post.get('client_email', '')
            vals['client_street'] = post.get('client_street', '')
            vals['client_street2'] = post.get('client_street2', '')
            vals['client_city'] = post.get('client_city', '')
            state_id = post.get('client_state_id')
            if state_id:
                vals['client_state_id'] = int(state_id)
            vals['client_zip'] = post.get('client_zip', '')
            country_id = post.get('client_country_id')
            if country_id:
                vals['client_country_id'] = int(country_id)
            dob = post.get('client_dob')
            if dob:
                vals['client_dob'] = dob

        vals['client_health_card'] = post.get('client_health_card', '')

        # Equipment
        vals['equipment_type'] = post.get('equipment_type', 'manual_wheelchair')
        vals['wheelchair_type'] = post.get('wheelchair_type', '')
        vals['powerchair_type'] = post.get('powerchair_type', '')
        vals['client_type'] = post.get('client_type', 'reg')
        vals['build_type'] = post.get('build_type', 'modular')
        vals['reason_for_application'] = post.get('reason_for_application', '')

        # Authorizer
        authorizer_id = post.get('authorizer_id')
        if authorizer_id:
            vals['authorizer_id'] = int(authorizer_id)

        # Measurements
        for field in ['seat_width', 'seat_depth', 'finished_seat_to_floor_height',
                       'back_cane_height', 'finished_back_height',
                       'finished_leg_rest_length', 'client_weight']:
            val = post.get(field)
            if val:
                try:
                    vals[field] = float(val)
                except (ValueError, TypeError):
                    pass

        # Measurement units
        for field in ['seat_width_unit', 'seat_depth_unit', 'seat_to_floor_unit',
                       'cane_height_unit', 'back_height_unit', 'leg_rest_unit',
                       'client_weight_unit']:
            val = post.get(field)
            if val:
                vals[field] = val

        # Frame
        frame_tmpl = post.get('frame_product_tmpl_id')
        if frame_tmpl:
            vals['frame_product_tmpl_id'] = int(frame_tmpl)
        frame_product = post.get('frame_product_id')
        if frame_product:
            vals['frame_product_id'] = int(frame_product)
        vals['frame_notes'] = post.get('frame_notes', '')
        vals['notes'] = post.get('notes', '')

        # Current step
        step = post.get('current_step')
        if step:
            vals['current_step'] = int(step)

        # ── Dynamic / equipment-specific fields → equipment_data_json ──
        # Collect any POST keys not in the known set — these come from
        # dynamic measurement / custom steps defined via fields_json
        equipment_data = {}
        for key, value in post.items():
            if key not in self._KNOWN_POST_KEYS and not key.startswith('section_'):
                # Skip empty values
                if value:
                    equipment_data[key] = value
        if equipment_data:
            # Merge with existing equipment_data_json if editing
            assessment_id = int(post.get('assessment_id') or 0)
            if assessment_id:
                assessment = request.env['fusion.wc.assessment'].sudo().browse(assessment_id)
                if assessment.exists() and assessment.equipment_data_json:
                    try:
                        existing = json.loads(assessment.equipment_data_json)
                        if isinstance(existing, dict):
                            existing.update(equipment_data)
                            equipment_data = existing
                    except (ValueError, TypeError):
                        pass
            vals['equipment_data_json'] = json.dumps(equipment_data)

        return vals

    def _process_assessment_lines(self, assessment, post):
        """Process selected products from the form into assessment lines."""
        AssessmentLine = request.env['fusion.wc.assessment.line'].sudo()

        # Remove existing non-upcharge lines (will be recreated from form)
        assessment.line_ids.filtered(lambda l: not l.is_upcharge).unlink()

        # Parse lines from POST data
        # Format: line_product_ids = comma-separated product IDs
        # line_sections = comma-separated section IDs (parallel array)
        # line_build_types = comma-separated build types (parallel array)
        line_products = post.get('line_product_ids', '').split(',')
        line_sections = post.get('line_section_ids', '').split(',')
        line_build_types = post.get('line_build_types', '').split(',')
        line_quantities = post.get('line_quantities', '').split(',')
        line_rationales = post.get('line_rationales', '').split('|||')

        for idx, tmpl_id_str in enumerate(line_products):
            if not tmpl_id_str.strip():
                continue
            try:
                tmpl_id = int(tmpl_id_str.strip())
            except (ValueError, TypeError):
                continue

            # Resolve product template → first active variant
            tmpl = request.env['product.template'].sudo().browse(tmpl_id)
            if not tmpl.exists():
                continue
            product = tmpl.product_variant_ids[:1]
            if not product:
                continue

            section_id = False
            if idx < len(line_sections) and line_sections[idx].strip():
                try:
                    section_id = int(line_sections[idx].strip())
                except (ValueError, TypeError):
                    pass

            build_type = False
            if idx < len(line_build_types) and line_build_types[idx].strip():
                build_type = line_build_types[idx].strip()

            quantity = 1.0
            if idx < len(line_quantities) and line_quantities[idx].strip():
                try:
                    quantity = float(line_quantities[idx].strip())
                except (ValueError, TypeError):
                    pass

            rationale = ''
            if idx < len(line_rationales):
                rationale = line_rationales[idx].strip()

            tmpl = product.product_tmpl_id
            AssessmentLine.create({
                'assessment_id': assessment.id,
                'section_id': section_id,
                'product_id': product.id,
                'product_name': product.display_name,
                'quantity': quantity,
                'adp_device_code': tmpl.x_fc_adp_device_code or '',
                'adp_price': tmpl.x_fc_adp_price or 0.0,
                'unit_price': product.lst_price,
                'build_type': build_type if build_type in ('modular', 'custom_fabricated') else False,
                'clinical_rationale': rationale,
            })


class QuotationPublic(http.Controller):
    """Public (token-based) access to the assessment form — no login required."""

    def _validate_token(self, token):
        """Look up and validate an assessment by its access token."""
        assessment = request.env['fusion.wc.assessment'].sudo().search([
            ('access_token', '=', token),
        ], limit=1)
        if not assessment:
            return None, 'not_found'
        if assessment.state == 'cancelled':
            return assessment, 'cancelled'
        return assessment, 'ok'

    # ------------------------------------------------------------------
    # Public form — full portal chrome
    # ------------------------------------------------------------------
    @http.route('/quotation/form/<string:token>', type='http',
                auth='public', website=True, sitemap=False)
    def public_form_view(self, token, **kw):
        """Render the assessment form for public (unauthenticated) users."""
        assessment, status = self._validate_token(token)
        if status != 'ok':
            return request.render('fusion_quotations.public_form_invalid', {
                'status': status,
            })

        portal_ctrl = QuotationPortal()
        ctx = portal_ctrl._get_form_context(
            assessment=assessment,
            requested_equipment_type=kw.get('equipment_type'),
        )
        ctx['page_name'] = 'wc_assessment_public'
        ctx['is_public'] = True
        ctx['access_token'] = token
        return request.render('fusion_quotations.portal_quotation_form', ctx)

    # ------------------------------------------------------------------
    # Embeddable form — no portal chrome, iframe-friendly
    # ------------------------------------------------------------------
    @http.route('/quotation/form/<string:token>/embed', type='http',
                auth='public', website=True, sitemap=False)
    def embed_form_view(self, token, **kw):
        """Render a minimal, iframe-embeddable version of the form."""
        assessment, status = self._validate_token(token)
        if status != 'ok':
            return request.render('fusion_quotations.public_form_invalid', {
                'status': status,
            })

        portal_ctrl = QuotationPortal()
        ctx = portal_ctrl._get_form_context(assessment=assessment)
        ctx['page_name'] = 'wc_assessment_embed'
        ctx['is_public'] = True
        ctx['is_embed'] = True
        ctx['access_token'] = token
        resp = request.render('fusion_quotations.portal_quotation_form_embed', ctx)
        resp.headers['X-Frame-Options'] = 'ALLOWALL'
        resp.headers['Content-Security-Policy'] = ''
        return resp

    # ------------------------------------------------------------------
    # Public form save (POST)
    # ------------------------------------------------------------------
    @http.route('/quotation/form/<string:token>/save', type='http',
                auth='public', website=True, methods=['POST'],
                csrf=True, sitemap=False)
    def public_form_save(self, token, **post):
        """Save form data submitted from the public/embed form."""
        assessment, status = self._validate_token(token)
        if status != 'ok' or not assessment:
            return request.redirect('/')

        if assessment.state not in ('draft', 'review'):
            return request.redirect(f'/quotation/form/{token}?error=readonly')

        portal_ctrl = QuotationPortal()
        vals = portal_ctrl._extract_assessment_vals(post)
        assessment.sudo().write(vals)
        portal_ctrl._process_assessment_lines(assessment, post)

        if post.get('action') == 'generate':
            try:
                assessment.sudo().action_generate_quotation()
                return request.redirect(
                    f'/quotation/form/{token}?success=quotation_generated')
            except Exception as e:
                _logger.error("Public quotation generation failed: %s", str(e))
                return request.redirect(
                    f'/quotation/form/{token}?error={str(e)}')

        return request.redirect(f'/quotation/form/{token}?success=saved')
