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

from odoo import http
from odoo.http import request
import json
import logging

_logger = logging.getLogger(__name__)


class QuotationAPI(http.Controller):

    @http.route('/my/quotation/api/search_clients', type='json',
                auth='user', methods=['POST'])
    def search_clients(self, query='', limit=20, **kw):
        """Search existing clients by name, phone, or health card."""
        if not query or len(query) < 2:
            return []

        Partner = request.env['res.partner'].sudo()
        domain = [
            '|',
            ('name', 'ilike', query),
            ('phone', 'ilike', query),
        ]
        partners = Partner.search(domain, limit=limit, order='name')

        return [{
            'id': p.id,
            'name': p.name,
            'phone': p.phone or '',
            'email': p.email or '',
            'street': p.street or '',
            'city': p.city or '',
        } for p in partners]

    @http.route('/my/quotation/api/search_products', type='json',
                auth='user', methods=['POST'])
    def search_products(self, query='', section_code=None, limit=20, **kw):
        """Search product templates (parent products), optionally filtered by section.
        Returns templates with variant_count so the UI can show a configurator
        when multiple variants exist.
        """
        if not query or len(query) < 2:
            return []

        Template = request.env['product.template'].sudo()
        domain = [
            ('sale_ok', '=', True),
            '|', '|',
            ('name', 'ilike', query),
            ('default_code', 'ilike', query),
            ('x_fc_adp_device_code', 'ilike', query),
        ]

        # If section specified, filter by category
        if section_code:
            Section = request.env['fusion.wc.section'].sudo()
            section = Section.search([('code', '=', section_code)], limit=1)
            if section and section.product_category_id:
                domain.append(('categ_id', 'child_of', section.product_category_id.id))

        templates = Template.search(domain, limit=limit, order='name')

        results = []
        for t in templates:
            variant_count = len(t.product_variant_ids)
            has_configurable = bool(
                t.attribute_line_ids and variant_count > 1
            )
            # For single-variant templates, get the variant ID directly
            single_variant_id = (
                t.product_variant_ids[0].id
                if variant_count == 1 else False
            )
            results.append({
                'id': t.id,
                'name': t.name,
                'default_code': t.default_code or '',
                'adp_device_code': t.x_fc_adp_device_code or '',
                'adp_price': t.x_fc_adp_price or 0.0,
                'list_price': t.list_price,
                'variant_count': variant_count,
                'has_configurable_attributes': has_configurable,
                'single_variant_id': single_variant_id,
            })

        return results

    @http.route('/my/quotation/api/get_product_attributes', type='json',
                auth='user', methods=['POST'])
    def get_product_attributes(self, template_id=None, **kw):
        """Given a product template ID, return its configurable attributes
        with available values for each.
        """
        if not template_id:
            return []

        Template = request.env['product.template'].sudo()
        tmpl = Template.browse(int(template_id))
        if not tmpl.exists():
            return []

        attributes = []
        for line in tmpl.attribute_line_ids:
            values = []
            for val in line.value_ids:
                values.append({
                    'id': val.id,
                    'name': val.name,
                })
            attributes.append({
                'attribute_id': line.attribute_id.id,
                'attribute_name': line.attribute_id.name,
                'values': values,
            })

        return attributes

    @http.route('/my/quotation/api/resolve_variant', type='json',
                auth='user', methods=['POST'])
    def resolve_variant(self, template_id=None, attribute_value_ids=None, **kw):
        """Given a template ID and list of selected attribute value IDs,
        find and return the matching product.product variant.
        """
        if not template_id or not attribute_value_ids:
            return {'error': 'Missing template_id or attribute_value_ids'}

        Template = request.env['product.template'].sudo()
        tmpl = Template.browse(int(template_id))
        if not tmpl.exists():
            return {'error': 'Template not found'}

        # Convert to set for matching
        selected_set = set(int(v) for v in attribute_value_ids)

        # Search through variants to find the one matching all selected values
        for variant in tmpl.product_variant_ids:
            variant_values = set()
            for ptav in variant.product_template_attribute_value_ids:
                variant_values.add(ptav.product_attribute_value_id.id)

            if variant_values == selected_set:
                return {
                    'id': variant.id,
                    'name': variant.display_name,
                    'default_code': variant.default_code or '',
                    'list_price': variant.lst_price,
                }

        # No exact match found — return first variant as fallback
        fallback = tmpl.product_variant_ids[:1]
        if fallback:
            return {
                'id': fallback.id,
                'name': fallback.display_name,
                'default_code': fallback.default_code or '',
                'list_price': fallback.lst_price,
                'warning': 'No exact variant match found, using default variant.',
            }

        return {'error': 'No variants found for this template'}

    @http.route('/my/quotation/api/get_section_options', type='json',
                auth='user', methods=['POST'])
    def get_section_options(self, section_code=None, build_type=None, **kw):
        """Get standard options for a section."""
        if not section_code:
            return []

        Section = request.env['fusion.wc.section'].sudo()
        section = Section.search([('code', '=', section_code)], limit=1)
        if not section:
            return []

        domain = [('section_id', '=', section.id), ('is_standard', '=', True)]
        if build_type and build_type in ('modular', 'custom_fabricated'):
            domain.append(('available_build_types', 'in', [build_type, 'both']))

        options = request.env['fusion.wc.section.option'].sudo().search(
            domain, order='sequence')

        return [{
            'id': o.id,
            'product_tmpl_id': o.product_tmpl_id.id,
            'product_name': o.product_tmpl_id.display_name,
            'variant_count': o.variant_count,
            'adp_device_code': o.adp_device_code or '',
            'adp_price': o.adp_price or 0.0,
            'list_price': o.list_price or 0.0,
            'requires_clinical_rationale': o.requires_clinical_rationale,
            'available_build_types': o.available_build_types,
        } for o in options]

    @http.route('/my/quotation/api/check_upcharges', type='json',
                auth='user', methods=['POST'])
    def check_upcharges(self, assessment_data=None, **kw):
        """Check which upcharge rules would trigger for given measurements.
        Returns list of upcharges WITHOUT creating any records.
        """
        if not assessment_data:
            return []

        rules = request.env['fusion.wc.upcharge.rule'].sudo().search([
            ('active', '=', True),
        ])

        equipment_type = assessment_data.get('equipment_type', 'manual_wheelchair')
        triggered = []
        exclusive_triggered = {}

        # Normalize measurements
        def get_value(field, unit_field, default_unit='inches'):
            val = float(assessment_data.get(field, 0) or 0)
            unit = assessment_data.get(unit_field, default_unit)
            if unit == 'cm' and val:
                val = val / 2.54
            return val

        seat_width = get_value('seat_width', 'seat_width_unit')
        seat_depth = get_value('seat_depth', 'seat_depth_unit')
        back_height = get_value('finished_back_height', 'back_height_unit')
        client_weight = float(assessment_data.get('client_weight', 0) or 0)
        weight_unit = assessment_data.get('client_weight_unit', 'lbs')
        if weight_unit == 'kg' and client_weight:
            client_weight = client_weight * 2.20462

        # Get backrest width from lines or default to seat_width
        back_width = float(assessment_data.get('back_width', 0) or 0)
        if not back_width:
            back_width = seat_width

        measurement_map = {
            'seat_width': seat_width,
            'seat_depth': seat_depth,
            'back_width': back_width,
            'back_height': back_height,
        }

        for rule in rules.sorted('sequence'):
            if rule.equipment_type not in (equipment_type, 'both'):
                continue

            if rule.mutually_exclusive_group:
                if rule.mutually_exclusive_group in exclusive_triggered:
                    continue

            matched = False

            if rule.trigger_type == 'measurement':
                val = measurement_map.get(rule.measurement_field, 0)
                if val:
                    matched = self._compare(val, rule.comparison, rule.threshold_value)

            elif rule.trigger_type == 'weight':
                if client_weight > rule.weight_min:
                    if not rule.weight_max or client_weight <= rule.weight_max:
                        matched = True

            elif rule.trigger_type == 'dimension_mismatch':
                val1 = measurement_map.get(rule.compare_field_1, 0)
                val2 = measurement_map.get(rule.compare_field_2, 0)
                if val1 and val2 and abs(val1 - val2) > 0.01:
                    matched = True

            if matched:
                # Look up ADP price
                adp_device = request.env['fusion.adp.device.code'].sudo().search(
                    [('device_code', '=', rule.adp_device_code), ('active', '=', True)],
                    limit=1
                )
                triggered.append({
                    'rule_id': rule.id,
                    'name': rule.name,
                    'adp_device_code': rule.adp_device_code,
                    'adp_price': adp_device.adp_price if adp_device else 0,
                    'description': rule.description or '',
                    'measurement_field': rule.measurement_field or '',
                })
                if rule.mutually_exclusive_group:
                    exclusive_triggered[rule.mutually_exclusive_group] = True

        return triggered

    @http.route('/my/quotation/api/save_step', type='json',
                auth='user', methods=['POST'])
    def save_step(self, assessment_id=None, step=None, data=None, **kw):
        """Auto-save current step data as JSON for resume."""
        if not assessment_id:
            return {'success': False, 'error': 'No assessment ID'}

        Assessment = request.env['fusion.wc.assessment'].sudo()
        assessment = Assessment.browse(int(assessment_id))

        if not assessment.exists() or assessment.sales_rep_id.id != request.env.user.id:
            return {'success': False, 'error': 'Access denied'}

        vals = {'current_step': int(step) if step else 1}
        if data:
            vals['form_data_json'] = json.dumps(data)

        assessment.write(vals)
        return {'success': True}

    @staticmethod
    def _compare(value, comparison, threshold):
        if comparison == 'gt':
            return value > threshold
        elif comparison == 'gte':
            return value >= threshold
        elif comparison == 'lt':
            return value < threshold
        elif comparison == 'eq':
            return abs(value - threshold) < 0.01
        elif comparison == 'neq':
            return abs(value - threshold) >= 0.01
        return False


class QuotationPublicAPI(http.Controller):
    """Public (token-based) JSON API — mirrors QuotationAPI for unauthenticated access."""

    def _validate_token(self, token):
        assessment = request.env['fusion.wc.assessment'].sudo().search([
            ('access_token', '=', token),
        ], limit=1)
        return assessment if assessment else None

    # --- delegates ---------------------------------------------------------
    # Each route validates the token then forwards to the existing API class.
    _api = QuotationAPI()

    @http.route('/quotation/api/<string:token>/search_clients',
                type='json', auth='public', methods=['POST'])
    def public_search_clients(self, token, **kw):
        if not self._validate_token(token):
            return {'error': 'invalid_token'}
        return self._api.search_clients(**kw)

    @http.route('/quotation/api/<string:token>/search_products',
                type='json', auth='public', methods=['POST'])
    def public_search_products(self, token, **kw):
        if not self._validate_token(token):
            return {'error': 'invalid_token'}
        return self._api.search_products(**kw)

    @http.route('/quotation/api/<string:token>/get_product_attributes',
                type='json', auth='public', methods=['POST'])
    def public_get_product_attributes(self, token, **kw):
        if not self._validate_token(token):
            return {'error': 'invalid_token'}
        return self._api.get_product_attributes(**kw)

    @http.route('/quotation/api/<string:token>/resolve_variant',
                type='json', auth='public', methods=['POST'])
    def public_resolve_variant(self, token, **kw):
        if not self._validate_token(token):
            return {'error': 'invalid_token'}
        return self._api.resolve_variant(**kw)

    @http.route('/quotation/api/<string:token>/get_section_options',
                type='json', auth='public', methods=['POST'])
    def public_get_section_options(self, token, **kw):
        if not self._validate_token(token):
            return {'error': 'invalid_token'}
        return self._api.get_section_options(**kw)

    @http.route('/quotation/api/<string:token>/check_upcharges',
                type='json', auth='public', methods=['POST'])
    def public_check_upcharges(self, token, **kw):
        if not self._validate_token(token):
            return {'error': 'invalid_token'}
        return self._api.check_upcharges(**kw)

    @http.route('/quotation/api/<string:token>/save_step',
                type='json', auth='public', methods=['POST'])
    def public_save_step(self, token, **kw):
        assessment = self._validate_token(token)
        if not assessment:
            return {'error': 'invalid_token'}
        # Override assessment_id from the token-linked assessment
        kw['assessment_id'] = assessment.id
        # Bypass user ownership check by handling directly
        vals = {'current_step': int(kw.get('step', 1))}
        if kw.get('data'):
            vals['form_data_json'] = json.dumps(kw['data'])
        assessment.sudo().write(vals)
        return {'success': True}
