import time
from typing import Any, Optional

from sqlalchemy import Integer, String, Float, JSON, Index, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Mapped, Session, mapped_column

from app.db.base import get_id_column, Base
from app.db.models._constraints import media_identity_constraint
from app.schemas.types import MUSIC_ENTITY_RECORDING, MediaSource


class Subscribe(Base):
    """
    订阅表
    """
    id = get_id_column()
    # 标题
    name: Mapped[str] = mapped_column(String, nullable=False, index=True)
    # 年份
    year: Mapped[Optional[str]] = mapped_column(String)
    # 类型
    type: Mapped[Optional[str]] = mapped_column(String)
    # 搜索关键字
    keyword: Mapped[Optional[str]] = mapped_column(String)
    media_source: Mapped[Optional[str]] = mapped_column(String, index=True)
    media_id: Mapped[Optional[str]] = mapped_column(String, index=True)
    # 音乐实体类型：recording 单曲、album 专辑
    music_type: Mapped[Optional[str]] = mapped_column(String)
    # 专辑预期总曲目数，供整专资源完整性判断
    total_tracks: Mapped[Optional[int]] = mapped_column(Integer)
    # 季号
    season: Mapped[Optional[int]] = mapped_column(Integer)
    # 海报
    poster: Mapped[Optional[str]] = mapped_column(String)
    # 背景图
    backdrop: Mapped[Optional[str]] = mapped_column(String)
    # 评分，float
    vote: Mapped[Optional[float]] = mapped_column(Float)
    # 简介
    description: Mapped[Optional[str]] = mapped_column(String)
    # 过滤规则
    filter: Mapped[Optional[str]] = mapped_column(String)
    # 包含
    include: Mapped[Optional[str]] = mapped_column(String)
    # 排除
    exclude: Mapped[Optional[str]] = mapped_column(String)
    # 质量
    quality: Mapped[Optional[str]] = mapped_column(String)
    # 分辨率
    resolution: Mapped[Optional[str]] = mapped_column(String)
    # 特效
    effect: Mapped[Optional[str]] = mapped_column(String)
    # 音乐音质等级：hires/lossless/lossy，可用正则组合
    audio_quality: Mapped[Optional[str]] = mapped_column(String)
    # 音频格式，可用正则组合
    audio_format: Mapped[Optional[str]] = mapped_column(String)
    # 最低码率（bps）
    min_bitrate: Mapped[Optional[int]] = mapped_column(Integer)
    # 最低位深（bit）
    min_bit_depth: Mapped[Optional[int]] = mapped_column(Integer)
    # 最低采样率（Hz）
    min_sample_rate: Mapped[Optional[int]] = mapped_column(Integer)
    # 总集数
    total_episode: Mapped[Optional[int]] = mapped_column(Integer)
    # 开始集数
    start_episode: Mapped[Optional[int]] = mapped_column(Integer)
    # 缺失集数
    lack_episode: Mapped[Optional[int]] = mapped_column(Integer)
    # 附加信息
    note: Mapped[Optional[Any]] = mapped_column(JSON)
    # 状态：N-新建 R-订阅中 P-待定 S-暂停
    state: Mapped[str] = mapped_column(String, nullable=False, index=True, default='N')
    # 最后更新时间
    last_update: Mapped[Optional[str]] = mapped_column(String)
    # 创建时间
    date: Mapped[Optional[str]] = mapped_column(String)
    # 订阅用户
    username: Mapped[Optional[str]] = mapped_column(String, index=True)
    # 订阅站点
    sites: Mapped[Optional[Any]] = mapped_column(JSON, default=list)
    # 下载器
    downloader: Mapped[Optional[str]] = mapped_column(String)
    # 是否洗版
    best_version: Mapped[Optional[int]] = mapped_column(Integer, default=0)
    # 是否只洗全集整包，开启后电视剧洗版不按单集下载
    best_version_full: Mapped[Optional[int]] = mapped_column(Integer, default=0)
    # 当前优先级
    current_priority: Mapped[Optional[int]] = mapped_column(Integer)
    # 当前音乐版本格式
    current_audio_format: Mapped[Optional[str]] = mapped_column(String)
    # 当前音乐版本码率（bps）
    current_bitrate: Mapped[Optional[int]] = mapped_column(Integer)
    # 当前音乐版本位深（bit）
    current_bit_depth: Mapped[Optional[int]] = mapped_column(Integer)
    # 当前音乐版本采样率（Hz）
    current_sample_rate: Mapped[Optional[int]] = mapped_column(Integer)
    # 洗版时已下载剧集的优先级状态，格式：{"1": 90, "2": 100}
    episode_priority: Mapped[Optional[Any]] = mapped_column(JSON)
    # 保存路径
    save_path: Mapped[Optional[str]] = mapped_column(String)
    # 是否使用 imdbid 搜索
    search_imdbid: Mapped[Optional[int]] = mapped_column(Integer, default=0)
    # 是否手动修改过总集数 0否 1是
    manual_total_episode: Mapped[Optional[int]] = mapped_column(Integer, default=0)
    # 自定义识别词
    custom_words: Mapped[Optional[str]] = mapped_column(String)
    # 自定义媒体类别
    media_category: Mapped[Optional[str]] = mapped_column(String)
    # 过滤规则组
    filter_groups: Mapped[Optional[Any]] = mapped_column(JSON, default=list)
    # 选择的剧集组
    episode_group: Mapped[Optional[str]] = mapped_column(String)

    __table_args__ = (
        media_identity_constraint("subscribe"),
        Index('ix_subscribe_type_date', 'type', 'date'),
        Index('ix_subscribe_media_identity', 'media_source', 'media_id'),
    )

    @classmethod
    def _identity_condition(
            cls,
            media_source: Optional[MediaSource] = None,
            media_id: Optional[str] = None,
            music_type: Optional[str] = None,
    ):
        """按统一媒体身份优先级构造订阅查询条件。"""
        if not media_source or media_id is None or not str(media_id).strip():
            return None
        condition = (
            (cls.media_source == str(media_source))
            & (cls.media_id == str(media_id).strip())
        )
        if music_type == MUSIC_ENTITY_RECORDING:
            return condition & or_(cls.music_type == music_type, cls.music_type.is_(None))
        if music_type:
            return condition & (cls.music_type == music_type)
        return condition

    @classmethod
    def exists(
            cls, db: Session,
            media_source: MediaSource | str | None = None,
            media_id: str | None = None,
            season: Optional[int] = None,
            episode_group: Optional[str] = None,
            music_type: Optional[str] = None,
    ):
        """按媒体身份、季号与剧集组查询已有订阅。"""
        condition = cls._identity_condition(
            media_source, media_id, music_type
        )
        if condition is None:
            return None
        statement = select(cls).where(condition)
        if season is not None:
            statement = statement.where(cls.season == season)
        return db.execute(
            statement.where(cls.episode_group == episode_group)
        ).scalars().first()

    @classmethod
    async def async_exists(
            cls, db: AsyncSession,
            media_source: MediaSource | str | None = None,
            media_id: str | None = None,
            season: Optional[int] = None,
            episode_group: Optional[str] = None,
            music_type: Optional[str] = None,
    ):
        """异步按媒体身份、季号与剧集组查询已有订阅。"""
        condition = cls._identity_condition(
            media_source, media_id, music_type
        )
        if condition is None:
            return None
        statement = select(cls).where(condition)
        if season is not None:
            statement = statement.where(cls.season == season)
        result = await db.execute(
            statement.where(cls.episode_group == episode_group)
        )
        return result.scalars().first()

    @classmethod
    def exists_by_username(
            cls, db: Session,
            username: str | MediaSource | None = None,
            media_source: MediaSource | str | None = None,
            media_id: str | None = None,
            season: Optional[int] = None,
            episode_group: Optional[str] = None,
            music_type: Optional[str] = None,
    ):
        """
        按订阅 owner、媒体身份、季号与剧集组查询订阅行。
        """
        if not username:
            return None
        condition = cls._identity_condition(
            media_source, media_id, music_type
        )
        if condition is None:
            return None
        statement = select(cls).where(cls.username == username, condition)
        if season is not None:
            statement = statement.where(cls.season == season)
        return db.execute(
            statement.where(cls.episode_group == episode_group)
        ).scalars().first()

    @classmethod
    async def async_exists_by_username(
            cls, db: AsyncSession,
            username: str | MediaSource | None = None,
            media_source: MediaSource | str | None = None,
            media_id: str | None = None, season: Optional[int] = None,
            episode_group: Optional[str] = None,
            music_type: Optional[str] = None,
    ):
        """
        异步按订阅 owner、媒体身份、季号与剧集组查询订阅行。
        """
        if not username:
            return None
        condition = cls._identity_condition(
            media_source, media_id, music_type
        )
        if condition is None:
            return None
        statement = select(cls).where(cls.username == username, condition)
        if season is not None:
            statement = statement.where(cls.season == season)
        result = await db.execute(
            statement.where(cls.episode_group == episode_group)
        )
        return result.scalars().first()

    @classmethod
    def get_by_state(cls, db: Session, state: str | None = None):
        """在调用方 Session 中按状态列表查询订阅。"""
        statement = select(cls)
        if state:
            statement = statement.where(cls.state.in_(state.split(',')))
        return list(db.execute(statement).scalars().all())

    @classmethod
    async def async_get_by_state(
        cls, db: AsyncSession, state: str | None = None
    ):
        """在调用方 AsyncSession 中按状态列表查询订阅。"""
        statement = select(cls)
        if state:
            statement = statement.where(cls.state.in_(state.split(',')))
        result = await db.execute(statement)
        return list(result.scalars().all())

    @classmethod
    def get_by_title(
        cls, db: Session, title: str,
        season: Optional[int] = None,
    ):
        """在调用方 Session 中按标题查询订阅。"""
        statement = select(cls).where(cls.name == title)
        if season is not None:
            statement = statement.where(cls.season == season)
        return db.execute(statement).scalars().first()

    @classmethod
    async def async_get_by_title(
        cls, db: AsyncSession, title: str,
        season: Optional[int] = None,
    ):
        """在调用方 AsyncSession 中按标题查询订阅。"""
        statement = select(cls).where(cls.name == title)
        if season is not None:
            statement = statement.where(cls.season == season)
        result = await db.execute(statement)
        return result.scalars().first()

    @classmethod
    async def async_list_by_title(
        cls, db: AsyncSession, title: str,
        season: Optional[int] = None,
    ):
        """在调用方 AsyncSession 中按标题查询候选订阅列表。"""
        statement = select(cls).where(cls.name == title)
        if season is not None:
            statement = statement.where(cls.season == season)
        result = await db.execute(statement)
        return list(result.scalars().all())

    @classmethod
    def list_by_media_identity(
            cls, db: Session,
            media_source: MediaSource | str | None = None,
            media_id: str | None = None,
            music_type: Optional[str] = None,
    ):
        """同步按统一媒体身份查询候选订阅列表。"""
        condition = cls._identity_condition(
            media_source=media_source,
            media_id=media_id,
            music_type=music_type,
        )
        if condition is None:
            return []
        return list(db.execute(select(cls).where(condition)).scalars().all())

    @classmethod
    async def async_list_by_media_identity(
            cls, db: AsyncSession,
            media_source: MediaSource | str | None = None,
            media_id: str | None = None,
            music_type: Optional[str] = None,
    ):
        """异步按统一媒体身份查询候选订阅列表。"""
        condition = cls._identity_condition(
            media_source=media_source,
            media_id=media_id,
            music_type=music_type,
        )
        if condition is None:
            return []
        result = await db.execute(select(cls).where(condition))
        return list(result.scalars().all())

    @classmethod
    def get_by(
            cls, db: Session,
            type: str | MediaSource | None = None,
            media_source: MediaSource | str | None = None,
            media_id: str | None = None,
            season: Optional[str] = None,
            music_type: Optional[str] = None,
    ):
        """
        根据条件查询订阅
        """
        condition = cls._identity_condition(
            media_source, media_id, music_type
        )
        if condition is None:
            return None
        statement = select(cls).where(condition, cls.type == type)
        if season is not None:
            statement = statement.where(cls.season == season)
        return db.execute(statement).scalars().first()

    @classmethod
    async def async_get_by(
            cls, db: AsyncSession,
            type: str | MediaSource | None = None,
            media_source: MediaSource | str | None = None,
            media_id: str | None = None,
            season: Optional[str] = None,
            music_type: Optional[str] = None,
    ):
        """
        根据条件查询订阅
        """
        condition = cls._identity_condition(
            media_source, media_id, music_type
        )
        if condition is None:
            return None
        query = select(cls).filter(condition, cls.type == type)
        if season is not None:
            query = query.filter(cls.season == season)
        result = await db.execute(query)
        return result.scalars().first()

    @classmethod
    def list_by_username(cls, db: Session, username: str,
                         state: Optional[str] = None, mtype: Optional[str] = None):
        """在调用方 Session 中按用户筛选订阅。"""
        statement = select(cls).where(cls.username == username)
        if state:
            statement = statement.where(cls.state == state)
        if mtype:
            statement = statement.where(cls.type == mtype)
        return list(db.execute(statement).scalars().all())

    @classmethod
    async def async_list_by_username(cls, db: AsyncSession,
                                     username: str, state: Optional[str] = None,
                                     mtype: Optional[str] = None):
        """在调用方 AsyncSession 中按用户筛选订阅。"""
        statement = select(cls).where(cls.username == username)
        if state:
            statement = statement.where(cls.state == state)
        if mtype:
            statement = statement.where(cls.type == mtype)
        result = await db.execute(statement)
        return list(result.scalars().all())

    @classmethod
    def list_by_type(cls, db: Session, mtype: str, days: int = 7):
        """在调用方 Session 中按类型查询最近时间窗内的订阅。"""
        return list(db.execute(select(cls).where(
            cls.type == mtype,
            cls.date >= time.strftime("%Y-%m-%d %H:%M:%S",
                                      time.localtime(time.time() - 86400 * int(days)))
        )).scalars().all())

    @classmethod
    async def async_list_by_type(cls, db: AsyncSession,
                                 mtype: str, days: int = 7):
        """在调用方 AsyncSession 中按类型查询最近时间窗内的订阅。"""
        result = await db.execute(select(cls).where(
            cls.type == mtype,
            cls.date >= time.strftime("%Y-%m-%d %H:%M:%S",
                                      time.localtime(time.time() - 86400 * int(days)))
        ))
        return list(result.scalars().all())
