"""Repository for dropdown options (channels, sub-channels, proof types)."""
import uuid
from typing import Optional
from sqlalchemy import select, delete
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.models.models import DropdownOption


class DropdownRepository:
    """Repository for managing dropdown options."""

    def __init__(self, session: AsyncSession):
        self.session = session

    async def get_all_hierarchical(self) -> dict:
        """
        Get all dropdown options as a hierarchical structure.
        Returns: {
            channels: { channel_name: { sub_channel_name: [proof_types] } },
            brand_guidelines: [brand_names],
            campaigns: []
        }
        """
        # Get all channels (top-level, no parent)
        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "channel",
            DropdownOption.parent_id.is_(None)
        ).options(selectinload(DropdownOption.children).selectinload(DropdownOption.children))

        result = await self.session.execute(stmt)
        channels = result.scalars().all()

        # Build hierarchical structure for channels
        hierarchy: dict[str, dict[str, list[str]]] = {}

        for channel in channels:
            channel_name = channel.value
            hierarchy[channel_name] = {}

            for sub_channel in channel.children:
                sub_channel_name = sub_channel.value
                hierarchy[channel_name][sub_channel_name] = [
                    pt.value for pt in sub_channel.children
                ]

        # Get brand guidelines
        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "brand_guideline"
        ).order_by(DropdownOption.display_order)

        result = await self.session.execute(stmt)
        brand_guidelines = [bg.value for bg in result.scalars().all()]

        return {
            "channels": hierarchy,
            "brand_guidelines": brand_guidelines,
            "campaigns": []  # campaigns not used from dropdown options
        }

    async def add_channel(self, name: str) -> DropdownOption:
        """Add a new channel (top-level option)."""
        option = DropdownOption(
            option_type="channel",
            value=name,
            parent_id=None
        )
        self.session.add(option)
        await self.session.flush()
        return option

    async def add_sub_channel(self, channel_name: str, sub_channel_name: str) -> Optional[DropdownOption]:
        """Add a sub-channel under a channel."""
        # Find the parent channel
        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "channel",
            DropdownOption.value == channel_name
        )
        result = await self.session.execute(stmt)
        channel = result.scalar_one_or_none()

        if not channel:
            return None

        option = DropdownOption(
            option_type="sub_channel",
            value=sub_channel_name,
            parent_id=channel.id
        )
        self.session.add(option)
        await self.session.flush()
        return option

    async def add_proof_type(self, channel_name: str, sub_channel_name: str, proof_type_name: str) -> Optional[DropdownOption]:
        """Add a proof type under a sub-channel."""
        # Find the parent sub-channel by traversing from channel
        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "channel",
            DropdownOption.value == channel_name
        )
        result = await self.session.execute(stmt)
        channel = result.scalar_one_or_none()

        if not channel:
            return None

        # Find sub-channel
        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "sub_channel",
            DropdownOption.value == sub_channel_name,
            DropdownOption.parent_id == channel.id
        )
        result = await self.session.execute(stmt)
        sub_channel = result.scalar_one_or_none()

        if not sub_channel:
            return None

        option = DropdownOption(
            option_type="proof_type",
            value=proof_type_name,
            parent_id=sub_channel.id
        )
        self.session.add(option)
        await self.session.flush()
        return option

    async def remove_channel(self, channel_name: str) -> bool:
        """Remove a channel and all its children (cascades)."""
        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "channel",
            DropdownOption.value == channel_name
        ).options(selectinload(DropdownOption.children).selectinload(DropdownOption.children))

        result = await self.session.execute(stmt)
        channel = result.scalar_one_or_none()

        if not channel:
            return False

        # Delete proof types, then sub-channels, then channel
        for sub_channel in channel.children:
            for proof_type in sub_channel.children:
                await self.session.delete(proof_type)
            await self.session.delete(sub_channel)
        await self.session.delete(channel)
        await self.session.flush()
        return True

    async def remove_sub_channel(self, channel_name: str, sub_channel_name: str) -> bool:
        """Remove a sub-channel and its proof types."""
        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "channel",
            DropdownOption.value == channel_name
        )
        result = await self.session.execute(stmt)
        channel = result.scalar_one_or_none()

        if not channel:
            return False

        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "sub_channel",
            DropdownOption.value == sub_channel_name,
            DropdownOption.parent_id == channel.id
        ).options(selectinload(DropdownOption.children))

        result = await self.session.execute(stmt)
        sub_channel = result.scalar_one_or_none()

        if not sub_channel:
            return False

        # Delete proof types, then sub-channel
        for proof_type in sub_channel.children:
            await self.session.delete(proof_type)
        await self.session.delete(sub_channel)
        await self.session.flush()
        return True

    async def remove_proof_type(self, channel_name: str, sub_channel_name: str, proof_type_name: str) -> bool:
        """Remove a proof type."""
        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "channel",
            DropdownOption.value == channel_name
        )
        result = await self.session.execute(stmt)
        channel = result.scalar_one_or_none()

        if not channel:
            return False

        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "sub_channel",
            DropdownOption.value == sub_channel_name,
            DropdownOption.parent_id == channel.id
        )
        result = await self.session.execute(stmt)
        sub_channel = result.scalar_one_or_none()

        if not sub_channel:
            return False

        stmt = select(DropdownOption).where(
            DropdownOption.option_type == "proof_type",
            DropdownOption.value == proof_type_name,
            DropdownOption.parent_id == sub_channel.id
        )
        result = await self.session.execute(stmt)
        proof_type = result.scalar_one_or_none()

        if not proof_type:
            return False

        await self.session.delete(proof_type)
        await self.session.flush()
        return True
