from typing import List, Optional
from common.logging import get_logger, span
from common.database import DBConfig, Db
from common.models.metric import ClipFilter, MetricResponse, ByPlatform, Users


logger = get_logger(__name__)

SELECT_ACTIVE_STREAMS_BY_PLATFORM = """
    SELECT
    platform,
    COUNT(*) AS active_streams
    FROM streams
    WHERE stream_end_time IS NULL
    GROUP BY platform
    """

SELECT_AI_CLIPS_GENERATED_TODAY = """
    SELECT COUNT(*) AS voice_today
    FROM clips
    WHERE kind = 'ai'
    AND deleted_at IS NULL
    AND created_at::date = CURRENT_DATE
    """

SELECT_VOICE_CLIPS_GENERATED_TODAY = """
    SELECT COUNT(*) AS voice_today
    FROM clips
    WHERE kind = 'voice'
    AND deleted_at IS NULL
    AND created_at::date = CURRENT_DATE
    """

SELECT_AI_CLIPS_GENERATED = """
    SELECT COUNT(*) AS voice_today
    FROM clips
    WHERE kind = 'ai'
    AND deleted_at IS NULL
    """

SELECT_VOICE_CLIPS_GENERATED = """
    SELECT COUNT(*) AS voice_today
    FROM clips
    WHERE kind = 'voice'
    AND deleted_at IS NULL
    """

SELECT_USER_WITH_BOTH_PLATFORM_LINKED = """ 
    WHERE u.deleted_at IS NULL
    AND EXISTS (
        SELECT 1 FROM twitch_account t
        WHERE t.user_id = u.id AND t.deleted_at IS NULL
    )
    AND EXISTS (
        SELECT 1 FROM kick_account k
        WHERE k.user_id = u.id AND k.deleted_at IS NULL
    );
    """

SELECT_USER_ONLY_TWITCH_LINKED = """
    SELECT COUNT(*) AS users_only_twitch
    FROM users u
    WHERE u.deleted_at IS NULL
    AND EXISTS (
        SELECT 1 FROM twitch_account t
        WHERE t.user_id = u.id AND t.deleted_at IS NULL
    )
    AND NOT EXISTS (
        SELECT 1 FROM kick_account k
        WHERE k.user_id = u.id AND k.deleted_at IS NULL
    );
    """

SELECT_USER_ONLY_KICK_LINKED = """
    SELECT COUNT(*) AS users_only_kick
    FROM users u
    WHERE u.deleted_at IS NULL
    AND EXISTS (
        SELECT 1 FROM kick_account k
        WHERE k.user_id = u.id AND k.deleted_at IS NULL
    )
    AND NOT EXISTS (
        SELECT 1 FROM twitch_account t
        WHERE t.user_id = u.id AND t.deleted_at IS NULL
    );
    """

SELECT_TOP_10_CLIP_WITH_MORE_VIEWS = """
    SELECT c.id
    FROM clips AS c
    JOIN clip_analytics AS ca ON ca.clip_id = c.id
    ORDER BY ca.views DESC
    LIMIT 10
    """

SELECT_TOP_10_CLIP_WITH_MORE_REACTIONS = """
    SELECT ca.clip_id
    FROM clip_analytics AS ca
    ORDER BY
    COALESCE(ca.heart_reactions, 0)
    + COALESCE(ca.smile_reactions, 0)
    + COALESCE(ca.laugh_reactions, 0)
    + COALESCE(ca.sign_reactions, 0)
    + COALESCE(ca.surprised_reactions, 0) DESC
    LIMIT 10
    """

SELECT_EDITED_CLIPS_GENERATED = """
    SELECT COUNT(*) AS edited_clips_total_active
    FROM edited_clips
    WHERE deleted_at IS NULL
    """

SELECT_EDITED_CLIPS_GENERATED_TODAY = """
    SELECT COUNT(*) AS edited_clips_today_active
    FROM edited_clips
    WHERE created_at::date = CURRENT_DATE
    AND deleted_at IS NULL
    """

SELECT_SCHEDULED_POST_COUNT = """
    SELECT COUNT(*) AS scheduled_total
    FROM scheduled_post
    """


SELECT_SCHEDULED_POST_COUNT_TODAY = """
    SELECT COUNT(*) AS scheduled_today
    FROM scheduled_post
    WHERE scheduled_datetime::date = CURRENT_DATE
    """

SELECT_NEW_USERS_LAST_48_HOURS = """
    SELECT COUNT(*) AS users_last_48h
    FROM users
    WHERE deleted_at IS NULL
    AND created_at >= NOW() - INTERVAL '48 hours'
    """

SELECT_TOTAL_USERS = """
    SELECT COUNT(*) AS users_total
    FROM users
    WHERE deleted_at IS NULL
    """

SELECT_TOP_10_USERS_MORE_CLIPS = """
    SELECT
    u.email,
    COUNT(*) AS clip_count
    FROM users AS u
    JOIN clips AS c
    ON c.user_id = u.id
    WHERE u.deleted_at IS NULL
    AND c.deleted_at IS NULL
    GROUP BY u.id, u.email
    ORDER BY clip_count DESC
    LIMIT 10
    """

SELECT_TOP_10_USERS_MORE_STREAMS = """
    SELECT
    u.email,
    COUNT(*) AS stream_count
    FROM users AS u
    JOIN streams AS s
    ON s.user_id = u.id
    WHERE u.deleted_at IS NULL
    GROUP BY u.id, u.email
    ORDER BY stream_count DESC
    LIMIT 10
    """

SELECT_TOP_10_USERS_STREAM_DURATION = """
    SELECT
    u.email,
    FLOOR(
        SUM(EXTRACT(EPOCH FROM (COALESCE(s.stream_end_time, NOW()) - s.stream_start_time))) / 3600
    )::int AS hours_streamed
    FROM streams AS s
    JOIN users AS u
    ON s.user_id = u.id
    GROUP BY u.email
    ORDER BY hours_streamed DESC
    LIMIT 10
    """

SELECT_MINUTES_SINCE_LAST_CLIP = """
    SELECT COALESCE(
        FLOOR(
            EXTRACT(EPOCH FROM (NOW() - (MAX(created_at) AT TIME ZONE 'UTC')))
            / 60
        )::int,
        0
        ) AS minutes_since_last_clip
        FROM clips
        WHERE deleted_at IS NULL;
"""

SELECT_STREAMS_INGESTED = """
    SELECT COUNT(*) AS streams_ingested
    FROM streams
"""

SELECT_STREAMS_INGESTED_TODAY = """
    SELECT COUNT(*) AS streams_ingested_today
    FROM streams
    WHERE stream_start_time::date = CURRENT_DATE
"""

SELECT_STREAMS_WITHOUT_CLIPS_TODAY = """
    SELECT COUNT(*) AS streams_without_clips_today
    FROM streams s
    WHERE s.stream_start_time::date = CURRENT_DATE
      AND NOT EXISTS (
        SELECT 1
        FROM clips c
        WHERE c.stream_id = s.id
      )
"""


class MetricDAO:
    """Data Access Object for metrics in PostgreSQL."""

    def __init__(self, db: Optional[Db] = None):
        """Initialize with an existing DB connection or create a new one."""
        self.db = db if db else Db(DBConfig())
        logger.debug("Metric initialized", extra={"component": "MetricDAO"})

    async def get_metrics(self) -> MetricResponse:
        """Get metrics for internal dashboard"""
        with span(logger, "get_metrics"):
            active_streams_by_platform: List[ByPlatform] = []
            active_streams_count: int = 0
            clips_generated: int = 0
            clips_generated_today: int = 0
            ai_clips_generated: int = 0
            voice_clips_generated: int = 0
            ai_clips_generated_today: int = 0
            voice_clips_generated_today: int = 0
            user_with_both_platform_linked: int = 0
            user_only_twitch_linked: int = 0
            user_only_kick_linked: int = 0
            top_10_clip_with_more_views: List[ClipFilter] = []
            top_10_clip_with_more_reactions: List[ClipFilter] = []
            edited_clips_generated: int = 0
            edited_clips_generated_today: int = 0
            scheduled_post_count: int = 0
            scheduled_post_count_today: int = 0
            new_users_last_48_hours: int = 0
            total_users: int = 0
            minutes_since_last_clip_generated: int = -1
            top_10_users_more_clips: List[Users] = []
            top_10_users_more_streams: List[Users] = []
            top_10_users_stream_duration: List[Users] = []
            streams_ingested: int = 0
            streams_ingested_today: int = 0
            streams_without_clips_today: int = 0

            result_1: list[tuple[object, ...]] = (
                await self.db.fetch_all(SELECT_ACTIVE_STREAMS_BY_PLATFORM) or []
            )
            if not result_1:
                logger.info("No active stream found for get_metrics")
            for row in result_1:
                data = ByPlatform(platform=row[0], count=row[1])
                active_streams_count = active_streams_count + data.count
                active_streams_by_platform.append(data)

            result_2 = await self.db.fetch_all(SELECT_AI_CLIPS_GENERATED_TODAY)
            if result_2:
                ai_clips_generated_today = result_2[0][0]

            result_3 = await self.db.fetch_all(SELECT_VOICE_CLIPS_GENERATED_TODAY)
            if result_3:
                voice_clips_generated_today = result_3[0][0]

            clips_generated_today = (
                ai_clips_generated_today + voice_clips_generated_today
            )

            result_4 = await self.db.fetch_all(SELECT_AI_CLIPS_GENERATED)
            if result_4:
                ai_clips_generated = result_4[0][0]

            result_5 = await self.db.fetch_all(SELECT_VOICE_CLIPS_GENERATED)
            if result_5:
                voice_clips_generated = result_5[0][0]

            clips_generated = ai_clips_generated + voice_clips_generated

            result_6 = await self.db.fetch_all(SELECT_USER_WITH_BOTH_PLATFORM_LINKED)
            if result_6:
                user_with_both_platform_linked = result_6[0][0]

            result_7 = await self.db.fetch_all(SELECT_USER_ONLY_TWITCH_LINKED)
            if result_7:
                user_only_twitch_linked = result_7[0][0]

            result_8 = await self.db.fetch_all(SELECT_USER_ONLY_KICK_LINKED)
            if result_8:
                user_only_twitch_linked = result_8[0][0]

            result_9 = await self.db.fetch_all(SELECT_TOP_10_CLIP_WITH_MORE_VIEWS)
            if result_9:
                for row in result_9:
                    data = ClipFilter(
                        id=row[0],
                        url=f"https://video-player.tl-dr.tv/?video_id={row[0]}",
                    )
                    top_10_clip_with_more_views.append(data)

            result_10 = await self.db.fetch_all(SELECT_TOP_10_CLIP_WITH_MORE_REACTIONS)
            if result_10:
                for row in result_10:
                    data = ClipFilter(
                        id=row[0],
                        url=f"https://video-player.tl-dr.tv/?video_id={row[0]}",
                    )
                    top_10_clip_with_more_reactions.append(data)

            result_11 = await self.db.fetch_all(SELECT_EDITED_CLIPS_GENERATED_TODAY)
            if result_11:
                edited_clips_generated_today = result_11[0][0]

            result_12 = await self.db.fetch_all(SELECT_EDITED_CLIPS_GENERATED)
            if result_12:
                edited_clips_generated = result_12[0][0]

            result_13 = await self.db.fetch_all(SELECT_SCHEDULED_POST_COUNT)
            if result_13:
                scheduled_post_count = result_13[0][0]

            result_14 = await self.db.fetch_all(SELECT_SCHEDULED_POST_COUNT_TODAY)
            if result_14:
                scheduled_post_count_today = result_14[0][0]

            result_15 = await self.db.fetch_all(SELECT_NEW_USERS_LAST_48_HOURS)
            if result_15:
                new_users_last_48_hours = result_15[0][0]

            result_16 = await self.db.fetch_all(SELECT_TOTAL_USERS)
            if result_16:
                total_users = result_16[0][0]

            result_17 = await self.db.fetch_all(SELECT_TOP_10_USERS_MORE_CLIPS)
            if result_17:
                for row in result_17:
                    data = Users(email=row[0], duration=row[1])
                    top_10_users_more_clips.append(data)

            result_18 = await self.db.fetch_all(SELECT_TOP_10_USERS_MORE_STREAMS)
            if result_18:
                for row in result_18:
                    data = Users(email=row[0], duration=row[1])
                    top_10_users_more_streams.append(data)

            result_19 = await self.db.fetch_all(SELECT_TOP_10_USERS_STREAM_DURATION)
            if result_19:
                for row in result_19:
                    data = Users(email=row[0], duration=row[1])
                    top_10_users_stream_duration.append(data)

            result_20 = await self.db.fetch_all(SELECT_MINUTES_SINCE_LAST_CLIP)
            if result_20:
                val = result_20[0][0]
                if val is not None:
                    minutes_since_last_clip_generated = int(val)

            result_21 = await self.db.fetch_all(SELECT_STREAMS_INGESTED)
            if result_21:
                streams_ingested = int(result_21[0][0] or 0)

            result_22 = await self.db.fetch_all(SELECT_STREAMS_INGESTED_TODAY)
            if result_22:
                streams_ingested_today = int(result_22[0][0] or 0)

            result_23 = await self.db.fetch_all(SELECT_STREAMS_WITHOUT_CLIPS_TODAY)
            if result_23:
                streams_without_clips_today = int(result_23[0][0] or 0)

            return MetricResponse(
                active_streams_count=int(active_streams_count),
                active_streams_by_platform=active_streams_by_platform,
                clips_generated=int(clips_generated),
                minutes_since_last_clip_generated=minutes_since_last_clip_generated,
                clips_generated_today=int(clips_generated_today),
                ai_clips_generated=int(ai_clips_generated),
                voice_clips_generated=int(voice_clips_generated),
                ai_clips_generated_today=int(ai_clips_generated_today),
                voice_clips_generated_today=int(voice_clips_generated_today),
                user_with_both_platform_linked=int(user_with_both_platform_linked),
                user_only_twitch_linked=int(user_only_twitch_linked),
                user_only_kick_linked=int(user_only_kick_linked),
                top_10_clip_with_more_views=top_10_clip_with_more_views,
                top_10_clip_with_more_reactions=top_10_clip_with_more_reactions,
                edited_clips_generated=int(edited_clips_generated),
                edited_clips_generated_today=int(edited_clips_generated_today),
                scheduled_post_count=int(scheduled_post_count),
                scheduled_post_count_today=int(scheduled_post_count_today),
                new_users_last_48_hours=int(new_users_last_48_hours),
                total_users=int(total_users),
                top_10_users_more_clips=top_10_users_more_clips,
                top_10_users_more_streams=top_10_users_more_streams,
                top_10_users_stream_duration=top_10_users_stream_duration,
                streams_ingested=int(streams_ingested),
                streams_ingested_today=int(streams_ingested_today),
                streams_without_clips_today=int(streams_without_clips_today),
            )
