from rapidfuzz import fuzz
from sqlalchemy.orm import Session

from app.core.exceptions import NotFoundError, ValidationError
from app.repositories.customer_repository import CustomerRepository
from app.repositories.policy_repository import PolicyRepository
from app.repositories.vehicle_repository import VehicleRepository
from app.schemas.customer import CustomerCreate, CustomerDedupMatch, CustomerResponse, CustomerUpdate
from app.services.audit_service import CUSTOMER_AUDIT_FIELDS, POLICY_AUDIT_FIELDS, AuditService
from app.services.policy_service import PolicyService
from app.services.vehicle_service import VehicleService


class CustomerService:
    def __init__(self, db: Session):
        self.db = db
        self.customers = CustomerRepository(db)

    def list_customers(self, agency_id: int, page: int, page_size: int, search: str | None):
        return self.customers.list_paginated(agency_id, page, page_size, search)

    def get_customer(self, agency_id: int, customer_id: int):
        customer = self.customers.get_by_id(customer_id, agency_id)
        if not customer:
            raise NotFoundError("Customer not found")
        return customer

    def create_customer(self, agency_id: int, payload: CustomerCreate, user_id: int | None = None):
        existing = self.customers.get_by_mobile(agency_id, payload.mobile)
        if existing:
            raise ValidationError("Customer with this mobile already exists", details=[{"field": "mobile", "error": "duplicate"}])
        customer = self.customers.create(agency_id, payload.model_dump())
        AuditService(self.db).log_create(
            agency_id=agency_id,
            user_id=user_id,
            entity_type="customer",
            entity_id=customer.id,
            fields=AuditService.snapshot_customer(customer),
            tracked_fields=CUSTOMER_AUDIT_FIELDS,
        )
        self.db.commit()
        self.db.refresh(customer)
        return customer

    def update_customer(self, agency_id: int, customer_id: int, payload: CustomerUpdate, user_id: int | None = None):
        customer = self.get_customer(agency_id, customer_id)
        before = AuditService.snapshot_customer(customer)
        if payload.mobile != customer.mobile:
            existing = self.customers.get_by_mobile(agency_id, payload.mobile)
            if existing and existing.id != customer_id:
                raise ValidationError("Customer with this mobile already exists")
        data = payload.model_dump(exclude_unset=True)
        customer = self.customers.update(customer, data)
        AuditService(self.db).log_update(
            agency_id=agency_id,
            user_id=user_id,
            entity_type="customer",
            entity_id=customer.id,
            before=before,
            after=AuditService.snapshot_customer(customer),
            tracked_fields=CUSTOMER_AUDIT_FIELDS,
        )
        self.db.commit()
        self.db.refresh(customer)
        return customer

    def delete_customer(self, agency_id: int, customer_id: int, user_id: int | None = None) -> None:
        customer = self.get_customer(agency_id, customer_id)
        AuditService(self.db).log_delete(
            agency_id=agency_id,
            user_id=user_id,
            entity_type="customer",
            entity_id=customer.id,
        )
        self.customers.soft_delete(customer)
        self.db.commit()

    def check_duplicates(self, agency_id: int, name: str, mobile: str) -> list[CustomerDedupMatch]:
        matches: list[CustomerDedupMatch] = []
        exact = self.customers.get_by_mobile(agency_id, mobile)
        if exact:
            matches.append(CustomerDedupMatch(id=exact.id, name=exact.name, mobile=exact.mobile, match_type="mobile"))

        for customer in self.customers.list_by_agency(agency_id):
            if exact and customer.id == exact.id:
                continue
            score = fuzz.ratio(name.lower(), customer.name.lower())
            if score >= 85:
                matches.append(
                    CustomerDedupMatch(
                        id=customer.id,
                        name=customer.name,
                        mobile=customer.mobile,
                        match_type="name_fuzzy",
                        match_score=score,
                    )
                )
        return matches

    def get_history(self, agency_id: int, customer_id: int) -> dict:
        customer = self.get_customer(agency_id, customer_id)
        vehicles = VehicleService(self.db).list_for_customer(agency_id, customer_id)
        policies = [
            PolicyService(self.db)._to_response(p)
            for p in PolicyRepository(self.db).list_by_customer(agency_id, customer_id)
        ]
        return {
            "customer": CustomerResponse.model_validate(customer),
            "vehicles": vehicles,
            "policies": policies,
        }
