from sqlalchemy import or_
from sqlalchemy.orm import Session

from app.models.customer import Customer
from app.models.insurance_company import InsuranceCompany
from app.models.policy import Policy
from app.models.vehicle import Vehicle
from app.utils.search_highlight import highlight_segments


class SearchService:
    POLICY_FIELDS = [
        "policy_number",
        "customer_name",
        "mobile",
        "registration",
        "engine",
        "chassis",
        "insurer",
    ]

    CUSTOMER_FIELDS = ["name", "mobile", "email", "city"]

    def __init__(self, db: Session):
        self.db = db

    def search(self, agency_id: int, query: str, page: int = 1, page_size: int = 20) -> tuple[list[dict], int]:
        term = (query or "").strip()
        if len(term) < 2:
            return [], 0

        pattern = f"%{term}%"
        policy_items = self._search_policies(agency_id, pattern, term)
        customer_items = self._search_customers(agency_id, pattern, term)
        combined = policy_items + customer_items
        combined.sort(key=lambda x: x["title"].lower())
        total = len(combined)
        start = (page - 1) * page_size
        return combined[start : start + page_size], total

    def _search_policies(self, agency_id: int, pattern: str, term: str) -> list[dict]:
        rows = (
            self.db.query(Policy, Customer, Vehicle, InsuranceCompany)
            .join(Customer, Policy.customer_id == Customer.id)
            .outerjoin(Vehicle, Policy.vehicle_id == Vehicle.id)
            .outerjoin(InsuranceCompany, Policy.insurance_company_id == InsuranceCompany.id)
            .filter(Policy.agency_id == agency_id, Policy.deleted_at.is_(None))
            .filter(
                or_(
                    Policy.policy_number.ilike(pattern),
                    Customer.name.ilike(pattern),
                    Customer.mobile.ilike(pattern),
                    Vehicle.registration_number.ilike(pattern),
                    Vehicle.engine_number.ilike(pattern),
                    Vehicle.chassis_number.ilike(pattern),
                    InsuranceCompany.name.ilike(pattern),
                )
            )
            .order_by(Policy.policy_end_date.asc())
            .limit(100)
            .all()
        )
        items = []
        for policy, customer, vehicle, company in rows:
            field_values = {
                "policy_number": policy.policy_number,
                "customer_name": customer.name if customer else None,
                "mobile": customer.mobile if customer else None,
                "registration": vehicle.registration_number if vehicle else None,
                "engine": vehicle.engine_number if vehicle else None,
                "chassis": vehicle.chassis_number if vehicle else None,
                "insurer": company.name if company else None,
            }
            matched_field, matched_value = self._first_match(term, field_values)
            title = customer.name if customer else policy.policy_number or f"Policy #{policy.id}"
            items.append(
                {
                    "type": "policy",
                    "id": policy.id,
                    "title": title,
                    "title_segments": highlight_segments(title, term),
                    "subtitle": " · ".join(
                        filter(
                            None,
                            [
                                policy.policy_number,
                                vehicle.registration_number if vehicle else None,
                                policy.policy_end_date.isoformat() if policy.policy_end_date else None,
                                policy.status.value if policy.status else None,
                            ],
                        )
                    ),
                    "matched_field": matched_field,
                    "matched_value": matched_value,
                    "matched_value_segments": highlight_segments(matched_value, term),
                }
            )
        return items

    def _search_customers(self, agency_id: int, pattern: str, term: str) -> list[dict]:
        rows = (
            self.db.query(Customer)
            .filter(Customer.agency_id == agency_id, Customer.deleted_at.is_(None))
            .filter(
                or_(
                    Customer.name.ilike(pattern),
                    Customer.mobile.ilike(pattern),
                    Customer.email.ilike(pattern),
                    Customer.city.ilike(pattern),
                )
            )
            .order_by(Customer.name.asc())
            .limit(50)
            .all()
        )
        items = []
        for customer in rows:
            field_values = {
                "name": customer.name,
                "mobile": customer.mobile,
                "email": customer.email,
                "city": customer.city,
            }
            matched_field, matched_value = self._first_match(term, field_values)
            policy_count = (
                self.db.query(Policy.id)
                .filter(Policy.customer_id == customer.id, Policy.deleted_at.is_(None))
                .count()
            )
            items.append(
                {
                    "type": "customer",
                    "id": customer.id,
                    "title": customer.name,
                    "title_segments": highlight_segments(customer.name, term),
                    "subtitle": f"{customer.mobile or '—'} · {policy_count} policies",
                    "matched_field": matched_field,
                    "matched_value": matched_value,
                    "matched_value_segments": highlight_segments(matched_value, term),
                }
            )
        return items

    @staticmethod
    def _first_match(term: str, fields: dict[str, str | None]) -> tuple[str | None, str | None]:
        needle = term.lower()
        for key, value in fields.items():
            if value and needle in value.lower():
                return key, value
        return None, None
