from sqlalchemy.ext.asyncio import AsyncSession

from src.core.errors import AccessDeniedException, ValidationsError
from src.db.models.budget_form import BudgetForm
from src.db.models.role import UserRoleEnum
from src.repository.budget_form_repository import BudgetFormRepository
from src.db.models.app_user import AppUser
from src.db.models.form_phase import FormPhase
from src.domain.schemas import FormPhaseCreate, FormPhaseUpdate
from src.repository.form_phase_repository import FormPhaseRepository


class FormPhaseService:
    def __init__(self, db: AsyncSession):
        self.db = db
        self.fp_repo = FormPhaseRepository(db)
        self.bf_repo = BudgetFormRepository(db)

    async def get_list(
        self,
        budget_form_id: int,
        user: AppUser,
        sheet: str | None = None,
        phase_code: str | None = None,
    ) -> list[FormPhase]:
        return await self.fp_repo.get_list(
            budget_form_id=budget_form_id,
            sheet=sheet,
            phase_code=phase_code,
        )

    async def get(
        self, budget_form_id: int, sheet: str, phase_code: str, user: AppUser
    ) -> FormPhase | None:
        return await self.fp_repo.get(
            budget_form_id=budget_form_id,
            sheet=sheet,
            phase_code=phase_code,
        )

    def _validate_form_phase(
            self,
            form_phase: FormPhaseCreate | FormPhaseUpdate,
            budget_form: BudgetForm,
            sheet: str | None = None,
    ) -> None:
        errors = {}
        if budget_form.form_type.sheet_list:
            sheet = sheet or form_phase.sheet
            if sheet not in budget_form.form_type.sheet_list:
                errors["sheet"] = [f"У формы нет листа {sheet}"]
        if form_phase.column_keys and budget_form.form_type.section_list:
            for column_key in form_phase.column_keys:
                section = column_key.split(".")
                if section[0] not in budget_form.form_type.section_list:
                    if "column_keys" in errors:
                        errors["column_keys"].append(f"Колонка {column_key} некорректна")
                    else:
                        errors["column_keys"] = [f"Колонка {column_key} некорректна"]
        if errors:
            raise ValidationsError(errors=errors)


    async def create(
            self,  
            body: FormPhaseCreate, 
            user: AppUser,
            budget_form_id: int | None = None,
            budget_form: BudgetForm | None = None,
    ) -> FormPhase:
        assert budget_form or budget_form_id
        if user.role_id != UserRoleEnum.ADMIN:
            raise AccessDeniedException
        
        if not budget_form or budget_form.form_type is None:
            budget_form = await self.bf_repo.get(
                budget_form_id=budget_form_id or budget_form.id,
                load_form_type=True,
            )
        self._validate_form_phase(
            form_phase=body,
            budget_form=budget_form,
        )
        return await self.fp_repo.create(
            budget_form_id=budget_form_id or budget_form.id,
            sheet=body.sheet,
            phase_code=body.phase_code,
            role=body.role,
            column_keys=body.column_keys,
            opens_at=body.opens_at,
            closes_at=body.closes_at,
        )

    async def update(
            self,
            sheet: str,
            phase_code: str,
            body: FormPhaseUpdate,
            user: AppUser,
            budget_form_id: int | None = None,
            budget_form: BudgetForm | None = None,
    ) -> FormPhase:
        assert budget_form or budget_form_id
        if user.role_id != UserRoleEnum.ADMIN:
            raise AccessDeniedException
        
        if not budget_form or budget_form.form_type is None:
            budget_form = await self.bf_repo.get(
                budget_form_id=budget_form_id or budget_form.id,
                load_form_type=True,
            )
        self._validate_form_phase(
            form_phase=body,
            budget_form=budget_form,
            sheet=sheet,
        )

        data = body.model_dump(exclude_unset=True)
        if not data:
            return await self.fp_repo.get(
                budget_form_id=budget_form_id or budget_form.id,
                sheet=sheet,
                phase_code=phase_code,
            )
        return await self.fp_repo.update(
            budget_form_id=budget_form_id or budget_form.id,
            sheet=sheet,
            phase_code=phase_code,
            data=data,
        )

    async def delete(
        self, budget_form_id: int, sheet: str, phase_code: str, user: AppUser
    ) -> bool:
        return await self.fp_repo.delete(
            budget_form_id=budget_form_id,
            sheet=sheet,
            phase_code=phase_code,
        )
