from typing import List, Optional, Tuple, Dict, Any
from sqlalchemy.orm import Session
from sqlalchemy import select

from .models import Identifier, IdentifierDetail
from .dto import IdentifierCreate, IdentifierUpdate, IdentifierDetailCreate, IdentifierDetailUpdate


class IdentifierService:
    @staticmethod
    def get_all(
        db: Session,
        tenant_id: int,
        company_id: int,
        skip: int = 0,
        limit: int = 100,
        filters: Optional[Dict[str, Any]] = None
    ) -> Tuple[List[Identifier], int]:
        query = db.query(Identifier).filter(
            Identifier.tenant_id == tenant_id,
            Identifier.company_id == company_id
        )

        if filters:
            if filters.get("code"):
                query = query.filter(Identifier.code.ilike(f"%{filters['code']}%"))
            if filters.get("description"):
                query = query.filter(
                    Identifier.description.ilike(f"%{filters['description']}%")
                )

        total = query.count()
        items = query.offset(skip).limit(limit).all()
        return items, total

    @staticmethod
    def get_by_id(
        db: Session,
        id: int,
        tenant_id: int,
        company_id: int
    ) -> Optional[Identifier]:
        return db.query(Identifier).filter(
            Identifier.id == id,
            Identifier.tenant_id == tenant_id,
            Identifier.company_id == company_id
        ).first()

    @staticmethod
    def create(
        db: Session,
        data: IdentifierCreate,
        tenant_id: int,
        company_id: int
    ) -> Identifier:
        # Exclude company_id from data as it is passed separately
        data_dict = data.model_dump()
        if 'company_id' in data_dict:
            del data_dict['company_id']

        db_obj = Identifier(
            **data_dict,
            tenant_id=tenant_id,
            company_id=company_id
        )
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        return db_obj

    @staticmethod
    def update(
        db: Session,
        id: int,
        tenant_id: int,
        data: IdentifierUpdate,
        company_id: int
    ) -> Optional[Identifier]:
        db_obj = IdentifierService.get_by_id(db, id, tenant_id, company_id)
        if not db_obj:
            return None

        update_dict = data.model_dump(exclude_unset=True)
        for key, value in update_dict.items():
            setattr(db_obj, key, value)

        db.commit()
        db.refresh(db_obj)
        return db_obj

    @staticmethod
    def delete(
        db: Session,
        id: int,
        tenant_id: int,
        company_id: int
    ) -> bool:
        db_obj = IdentifierService.get_by_id(db, id, tenant_id, company_id)
        if not db_obj:
            return False

        db.delete(db_obj)
        db.commit()
        return True


class IdentifierDetailService:
    @staticmethod
    def get_all(
        db: Session,
        tenant_id: int,
        company_id: int,
        skip: int = 0,
        limit: int = 100,
        filters: Optional[Dict[str, Any]] = None
    ) -> Tuple[List[IdentifierDetail], int]:
        query = db.query(IdentifierDetail).filter(
            IdentifierDetail.tenant_id == tenant_id,
            IdentifierDetail.company_id == company_id
        )

        if filters:
            # Add filters here if needed
            pass

        total = query.count()
        items = query.offset(skip).limit(limit).all()
        return items, total

    @staticmethod
    def get_by_id(
        db: Session,
        id: int,
        tenant_id: int,
        company_id: int
    ) -> Optional[IdentifierDetail]:
        return db.query(IdentifierDetail).filter(
            IdentifierDetail.id == id,
            IdentifierDetail.tenant_id == tenant_id,
            IdentifierDetail.company_id == company_id
        ).first()

    @staticmethod
    def create(
        db: Session,
        data: IdentifierDetailCreate,
        tenant_id: int,
        company_id: int
    ) -> IdentifierDetail:
        # Exclude company_id from data as it is passed separately
        data_dict = data.model_dump()
        if 'company_id' in data_dict:
            del data_dict['company_id']

        db_obj = IdentifierDetail(
            **data_dict,
            tenant_id=tenant_id,
            company_id=company_id
        )
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        return db_obj

    @staticmethod
    def update(
        db: Session,
        id: int,
        data: IdentifierDetailUpdate,
        tenant_id: int,
        company_id: int
    ) -> Optional[IdentifierDetail]:
        db_obj = IdentifierDetailService.get_by_id(
            db, id, tenant_id, company_id)
        if not db_obj:
            return None

        update_dict = data.model_dump(exclude_unset=True)
        for key, value in update_dict.items():
            setattr(db_obj, key, value)

        db.commit()
        db.refresh(db_obj)
        return db_obj

    @staticmethod
    def delete(
        db: Session,
        id: int,
        tenant_id: int,
        company_id: int
    ) -> bool:
        db_obj = IdentifierDetailService.get_by_id(
            db, id, tenant_id, company_id)
        if not db_obj:
            return False

        db.delete(db_obj)
        db.commit()
        return True
