import { and, count, desc, eq, gte, inArray, sql } from "drizzle-orm";
import { z } from "zod";

import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import {
  activityLogs,
  experiments,
  participants,
  studies,
  studyMembers,
  trials,
  trialEvents,
  users,
  userSystemRoles,
} from "~/server/db/schema";

export const dashboardRouter = createTRPCRouter({
  getRecentActivity: protectedProcedure
    .input(
      z.object({
        limit: z.number().min(1).max(20).default(10),
        studyId: z.string().uuid().optional(),
      }),
    )
    .query(async ({ ctx, input }) => {
      const userId = ctx.session.user.id;

      // Get studies the user has access to
      const accessibleStudies = await ctx.db
        .select({ studyId: studyMembers.studyId })
        .from(studyMembers)
        .where(eq(studyMembers.userId, userId));

      const studyIds = accessibleStudies.map((s) => s.studyId);

      // If no accessible studies, return empty
      if (studyIds.length === 0) {
        return [];
      }

      // Build where conditions
      const whereConditions = input.studyId
        ? and(
            eq(experiments.studyId, input.studyId),
            inArray(trialEvents.eventType, [
              "trial_started",
              "trial_completed",
              "intervention",
              "error",
              "annotation",
            ]),
          )
        : and(
            inArray(experiments.studyId, studyIds),
            inArray(trialEvents.eventType, [
              "trial_started",
              "trial_completed",
              "intervention",
              "error",
              "annotation",
            ]),
          );

      // Get recent interesting trial events
      const activities = await ctx.db
        .select({
          id: trialEvents.id,
          type: trialEvents.eventType,
          data: trialEvents.data,
          timestamp: trialEvents.timestamp,
          trialId: trials.id,
          experimentName: experiments.name,
          participantCode: participants.participantCode,
          user: {
            name: users.name,
          },
        })
        .from(trialEvents)
        .innerJoin(trials, eq(trialEvents.trialId, trials.id))
        .innerJoin(experiments, eq(trials.experimentId, experiments.id))
        .innerJoin(participants, eq(trials.participantId, participants.id))
        .leftJoin(users, eq(trialEvents.createdBy, users.id))
        .where(whereConditions)
        .orderBy(desc(trialEvents.timestamp))
        .limit(input.limit);

      return activities.map((activity) => {
        let title = activity.type.replace(/_/g, " ");
        title = title.charAt(0).toUpperCase() + title.slice(1);

        let description = `${activity.participantCode} • ${activity.experimentName}`;
        if (activity.user?.name) {
          description += ` • by ${activity.user.name}`;
        }

        return {
          id: activity.id,
          type: activity.type,
          title: title,
          description: description,
          time: activity.timestamp,
          status:
            activity.type === "error"
              ? "error"
              : activity.type === "trial_completed"
                ? "success"
                : ("info" as const),
          data: activity.data,
          trialId: activity.trialId,
        };
      });
    }),

  getLiveTrials: protectedProcedure
    .input(
      z.object({
        studyId: z.string().uuid().optional(),
      }),
    )
    .query(async ({ ctx, input }) => {
      const userId = ctx.session.user.id;

      // Get studies the user has access to
      const accessibleStudies = await ctx.db
        .select({ studyId: studyMembers.studyId })
        .from(studyMembers)
        .where(eq(studyMembers.userId, userId));

      const studyIds = accessibleStudies.map((s) => s.studyId);

      if (studyIds.length === 0) return [];

      const whereConditions = input.studyId
        ? and(
            eq(experiments.studyId, input.studyId),
            eq(trials.status, "in_progress"),
          )
        : and(
            inArray(experiments.studyId, studyIds),
            eq(trials.status, "in_progress"),
          );

      const live = await ctx.db
        .select({
          id: trials.id,
          studyId: studies.id,
          startedAt: trials.startedAt,
          experimentName: experiments.name,
          participantCode: participants.participantCode,
          studyName: studies.name,
        })
        .from(trials)
        .innerJoin(experiments, eq(trials.experimentId, experiments.id))
        .innerJoin(participants, eq(trials.participantId, participants.id))
        .innerJoin(studies, eq(experiments.studyId, studies.id))
        .where(whereConditions)
        .orderBy(desc(trials.startedAt));

      return live;
    }),

  getStudyProgress: protectedProcedure
    .input(
      z.object({
        limit: z.number().min(1).max(10).default(5),
        studyId: z.string().uuid().optional(),
      }),
    )
    .query(async ({ ctx, input }) => {
      const userId = ctx.session.user.id;

      // Build where conditions
      const whereConditions = input.studyId
        ? and(
            eq(studyMembers.userId, userId),
            eq(studies.status, "active"),
            eq(studies.id, input.studyId),
          )
        : and(eq(studyMembers.userId, userId), eq(studies.status, "active"));

      // Get studies the user has access to with participant counts
      const studyProgress = await ctx.db
        .select({
          id: studies.id,
          name: studies.name,
          status: studies.status,
          createdAt: studies.createdAt,
          totalParticipants: count(participants.id),
        })
        .from(studies)
        .innerJoin(studyMembers, eq(studies.id, studyMembers.studyId))
        .leftJoin(participants, eq(studies.id, participants.studyId))
        .where(whereConditions)
        .groupBy(studies.id, studies.name, studies.status, studies.createdAt)
        .orderBy(desc(studies.createdAt))
        .limit(input.limit);

      // Get trial completion counts for each study
      const studyIds = studyProgress.map((s) => s.id);

      const trialCounts =
        studyIds.length > 0
          ? await ctx.db
              .select({
                studyId: experiments.studyId,
                completedTrials: count(trials.id),
              })
              .from(experiments)
              .innerJoin(trials, eq(experiments.id, trials.experimentId))
              .where(
                and(
                  inArray(experiments.studyId, studyIds),
                  eq(trials.status, "completed"),
                ),
              )
              .groupBy(experiments.studyId)
          : [];

      const trialCountMap = new Map(
        trialCounts.map((tc) => [tc.studyId, tc.completedTrials]),
      );

      return studyProgress.map((study) => {
        const completedTrials = trialCountMap.get(study.id) ?? 0;
        const totalParticipants = study.totalParticipants;

        // Calculate progress based on completed trials vs participants
        // If no participants, progress is 0; if trials >= participants, progress is 100%
        const progress =
          totalParticipants > 0
            ? Math.min(
                100,
                Math.round((completedTrials / totalParticipants) * 100),
              )
            : 0;

        return {
          id: study.id,
          name: study.name,
          progress,
          participants: completedTrials, // Using completed trials as active participants
          totalParticipants,
          status: study.status,
        };
      });
    }),

  getStats: protectedProcedure
    .input(
      z.object({
        studyId: z.string().uuid().optional(),
      }),
    )
    .query(async ({ ctx, input }) => {
      const userId = ctx.session.user.id;

      // Get studies the user has access to
      const accessibleStudies = await ctx.db
        .select({ studyId: studyMembers.studyId })
        .from(studyMembers)
        .where(eq(studyMembers.userId, userId));

      let studyIds = accessibleStudies.map((s) => s.studyId);

      // Filter to specific study if provided
      if (input.studyId) {
        // Verify user has access to the specific study
        if (studyIds.includes(input.studyId)) {
          studyIds = [input.studyId];
        } else {
          // User doesn't have access to this study
          studyIds = [];
        }
      }

      if (studyIds.length === 0) {
        return {
          totalStudies: 0,
          totalExperiments: 0,
          totalParticipants: 0,
          totalTrials: 0,
          activeTrials: 0,
          scheduledTrials: 0,
          completedToday: 0,
        };
      }

      // Get total counts
      const [studyCount] = await ctx.db
        .select({ count: count() })
        .from(studies)
        .where(inArray(studies.id, studyIds));

      const [experimentCount] = await ctx.db
        .select({ count: count() })
        .from(experiments)
        .where(inArray(experiments.studyId, studyIds));

      const [participantCount] = await ctx.db
        .select({ count: count() })
        .from(participants)
        .where(inArray(participants.studyId, studyIds));

      const [trialCount] = await ctx.db
        .select({ count: count() })
        .from(trials)
        .innerJoin(experiments, eq(trials.experimentId, experiments.id))
        .where(inArray(experiments.studyId, studyIds));

      // Get active trials count
      const [activeTrialsCount] = await ctx.db
        .select({ count: count() })
        .from(trials)
        .innerJoin(experiments, eq(trials.experimentId, experiments.id))
        .where(
          and(
            inArray(experiments.studyId, studyIds),
            eq(trials.status, "in_progress"),
          ),
        );

      // Get scheduled trials count
      const [scheduledTrialsCount] = await ctx.db
        .select({ count: count() })
        .from(trials)
        .innerJoin(experiments, eq(trials.experimentId, experiments.id))
        .where(
          and(
            inArray(experiments.studyId, studyIds),
            eq(trials.status, "scheduled"),
          ),
        );

      // Get today's completed trials
      const today = new Date();
      today.setHours(0, 0, 0, 0);

      const [completedTodayCount] = await ctx.db
        .select({ count: count() })
        .from(trials)
        .innerJoin(experiments, eq(trials.experimentId, experiments.id))
        .where(
          and(
            inArray(experiments.studyId, studyIds),
            eq(trials.status, "completed"),
            gte(trials.completedAt, today),
          ),
        );

      // Get total interventions
      const [interventionsCount] = await ctx.db
        .select({ count: count() })
        .from(trialEvents)
        .innerJoin(trials, eq(trialEvents.trialId, trials.id))
        .innerJoin(experiments, eq(trials.experimentId, experiments.id))
        .where(
          and(
            inArray(experiments.studyId, studyIds),
            eq(trialEvents.eventType, "intervention"),
          ),
        );

      return {
        totalStudies: studyCount?.count ?? 0,
        totalExperiments: experimentCount?.count ?? 0,
        totalParticipants: participantCount?.count ?? 0,
        totalTrials: trialCount?.count ?? 0,
        activeTrials: activeTrialsCount?.count ?? 0,
        scheduledTrials: scheduledTrialsCount?.count ?? 0,
        completedToday: completedTodayCount?.count ?? 0,
        totalInterventions: interventionsCount?.count ?? 0,
      };
    }),

  debug: protectedProcedure.query(async ({ ctx }) => {
    const userId = ctx.session.user.id;

    // Get user info
    const user = await ctx.db.query.users.findFirst({
      where: eq(users.id, userId),
    });

    // Get user system roles
    const systemRoles = await ctx.db.query.userSystemRoles.findMany({
      where: eq(userSystemRoles.userId, userId),
    });

    // Get user study memberships
    const studyMemberships = await ctx.db.query.studyMembers.findMany({
      where: eq(studyMembers.userId, userId),
      with: {
        study: {
          columns: {
            id: true,
            name: true,
            status: true,
          },
        },
      },
    });

    // Get all studies (admin view)
    const allStudies = await ctx.db.query.studies.findMany({
      columns: {
        id: true,
        name: true,
        status: true,
        createdBy: true,
      },
      where: sql`deleted_at IS NULL`,
      limit: 10,
    });

    return {
      user: user
        ? {
            id: user.id,
            email: user.email,
            name: user.name,
          }
        : null,
      systemRoles: systemRoles.map((r) => r.role),
      studyMemberships: studyMemberships.map((m) => ({
        studyId: m.studyId,
        role: m.role,
        study: m.study,
      })),
      allStudies,
      session: {
        userId: ctx.session.user.id,
        userEmail: ctx.session.user.email,
        userRole: systemRoles[0]?.role ?? null,
      },
    };
  }),
});
