import { Injectable } from "@nestjs/common";
import { PrismaService } from "../prisma/prisma.service";
import { TaskStatus, TaskPriority, ProjectStatus } from "@prisma/client";
import type { StatCardQueryDto, ChartQueryDto, ListQueryDto, CalendarPreviewQueryDto } from "./dto";

/**
 * Widget data response types
 */
export interface WidgetStatData {
  value: number;
  change?: number;
  changePercent?: number;
  previousValue?: number;
}

export interface WidgetChartData {
  labels: string[];
  datasets: {
    label: string;
    data: number[];
    backgroundColor?: string[];
  }[];
}

export interface WidgetListItem {
  id: string;
  title: string;
  subtitle?: string;
  status?: string;
  priority?: string;
  dueDate?: string;
  startTime?: string;
  color?: string;
}

export interface WidgetCalendarItem {
  id: string;
  title: string;
  startTime: string;
  endTime?: string;
  allDay?: boolean;
  type: "task" | "event";
  color?: string;
}

export interface WidgetProjectItem {
  id: string;
  name: string;
  status: string;
  lastActivity: string;
  taskCount: number;
  eventCount: number;
  color: string | null;
}

export interface WidgetAgentSessionItem {
  id: string;
  sessionKey: string;
  label: string | null;
  channel: string | null;
  agentName: string | null;
  agentStatus: string | null;
  status: "active" | "ended";
  startedAt: string;
  lastMessageAt: string | null;
  runtimeMs: number;
  messageCount: number;
  contextSummary: string | null;
}

/**
 * Service for fetching widget data from various sources
 */
@Injectable()
export class WidgetDataService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Get stat card data based on configuration
   */
  async getStatCardData(workspaceId: string, query: StatCardQueryDto): Promise<WidgetStatData> {
    const { dataSource, metric, filter } = query;

    switch (dataSource) {
      case "tasks":
        return this.getTaskStatData(workspaceId, metric, filter);
      case "events":
        return this.getEventStatData(workspaceId, metric, filter);
      case "projects":
        return this.getProjectStatData(workspaceId, metric, filter);
      default:
        return { value: 0 };
    }
  }

  /**
   * Get chart data based on configuration
   */
  async getChartData(workspaceId: string, query: ChartQueryDto): Promise<WidgetChartData> {
    const { dataSource, groupBy, filter, colors } = query;

    switch (dataSource) {
      case "tasks":
        return this.getTaskChartData(workspaceId, groupBy, filter, colors);
      case "events":
        return this.getEventChartData(workspaceId, groupBy, filter, colors);
      case "projects":
        return this.getProjectChartData(workspaceId, groupBy, filter, colors);
      default:
        return { labels: [], datasets: [] };
    }
  }

  /**
   * Get list data based on configuration
   */
  async getListData(workspaceId: string, query: ListQueryDto): Promise<WidgetListItem[]> {
    const { dataSource, sortBy, sortOrder, limit, filter } = query;

    switch (dataSource) {
      case "tasks":
        return this.getTaskListData(workspaceId, sortBy, sortOrder, limit, filter);
      case "events":
        return this.getEventListData(workspaceId, sortBy, sortOrder, limit, filter);
      case "projects":
        return this.getProjectListData(workspaceId, sortBy, sortOrder, limit, filter);
      default:
        return [];
    }
  }

  /**
   * Get calendar preview data
   */
  async getCalendarPreviewData(
    workspaceId: string,
    query: CalendarPreviewQueryDto
  ): Promise<WidgetCalendarItem[]> {
    const { showTasks = true, showEvents = true, daysAhead = 7 } = query;
    const items: WidgetCalendarItem[] = [];

    const startDate = new Date();
    startDate.setHours(0, 0, 0, 0);
    const endDate = new Date(startDate);
    endDate.setDate(endDate.getDate() + daysAhead);

    if (showEvents) {
      const events = await this.prisma.event.findMany({
        where: {
          workspaceId,
          startTime: {
            gte: startDate,
            lte: endDate,
          },
        },
        include: {
          project: {
            select: { color: true },
          },
        },
        orderBy: { startTime: "asc" },
        take: 20,
      });

      items.push(
        ...events.map((event) => {
          const item: WidgetCalendarItem = {
            id: event.id,
            title: event.title,
            startTime: event.startTime.toISOString(),
            allDay: event.allDay,
            type: "event" as const,
            color: event.project?.color ?? "#3B82F6",
          };
          if (event.endTime !== null) {
            item.endTime = event.endTime.toISOString();
          }
          return item;
        })
      );
    }

    if (showTasks) {
      const tasks = await this.prisma.task.findMany({
        where: {
          workspaceId,
          dueDate: {
            gte: startDate,
            lte: endDate,
          },
          status: {
            not: TaskStatus.COMPLETED,
          },
        },
        include: {
          project: {
            select: { color: true },
          },
        },
        orderBy: { dueDate: "asc" },
        take: 20,
      });

      items.push(
        ...tasks
          .filter((task): task is typeof task & { dueDate: Date } => task.dueDate !== null)
          .map((task) => ({
            id: task.id,
            title: task.title,
            startTime: task.dueDate.toISOString(),
            allDay: true,
            type: "task" as const,
            color: task.project?.color ?? "#10B981",
          }))
      );
    }

    // Sort by start time
    items.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());

    return items;
  }

  // Private helper methods

  private async getTaskStatData(
    workspaceId: string,
    metric: string,
    filter?: Record<string, unknown>
  ): Promise<WidgetStatData> {
    const where: Record<string, unknown> = { workspaceId, ...filter };

    switch (metric) {
      case "count": {
        const count = await this.prisma.task.count({ where });
        return { value: count };
      }
      case "completed": {
        const completed = await this.prisma.task.count({
          where: { ...where, status: TaskStatus.COMPLETED },
        });
        return { value: completed };
      }
      case "overdue": {
        const overdue = await this.prisma.task.count({
          where: {
            ...where,
            status: { not: TaskStatus.COMPLETED },
            dueDate: { lt: new Date() },
          },
        });
        return { value: overdue };
      }
      case "upcoming": {
        const nextWeek = new Date();
        nextWeek.setDate(nextWeek.getDate() + 7);
        const upcoming = await this.prisma.task.count({
          where: {
            ...where,
            status: { not: TaskStatus.COMPLETED },
            dueDate: { gte: new Date(), lte: nextWeek },
          },
        });
        return { value: upcoming };
      }
      default:
        return { value: 0 };
    }
  }

  private async getEventStatData(
    workspaceId: string,
    metric: string,
    filter?: Record<string, unknown>
  ): Promise<WidgetStatData> {
    const where: Record<string, unknown> = { workspaceId, ...filter };

    switch (metric) {
      case "count": {
        const count = await this.prisma.event.count({ where });
        return { value: count };
      }
      case "upcoming": {
        const nextWeek = new Date();
        nextWeek.setDate(nextWeek.getDate() + 7);
        const upcoming = await this.prisma.event.count({
          where: {
            ...where,
            startTime: { gte: new Date(), lte: nextWeek },
          },
        });
        return { value: upcoming };
      }
      default:
        return { value: 0 };
    }
  }

  private async getProjectStatData(
    workspaceId: string,
    metric: string,
    filter?: Record<string, unknown>
  ): Promise<WidgetStatData> {
    const where: Record<string, unknown> = { workspaceId, ...filter };

    switch (metric) {
      case "count": {
        const count = await this.prisma.project.count({ where });
        return { value: count };
      }
      case "completed": {
        const completed = await this.prisma.project.count({
          where: { ...where, status: ProjectStatus.COMPLETED },
        });
        return { value: completed };
      }
      default:
        return { value: 0 };
    }
  }

  private async getTaskChartData(
    workspaceId: string,
    groupBy: string,
    filter?: Record<string, unknown>,
    colors?: string[]
  ): Promise<WidgetChartData> {
    const where: Record<string, unknown> = { workspaceId, ...filter };
    const defaultColors = ["#3B82F6", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6"];

    switch (groupBy) {
      case "status": {
        const statusCounts = await this.prisma.task.groupBy({
          by: ["status"],
          where,
          _count: { id: true },
        });

        const statusLabels = Object.values(TaskStatus);
        const statusData = statusLabels.map((status) => {
          const found = statusCounts.find((s) => s.status === status);
          return found ? found._count.id : 0;
        });

        return {
          labels: statusLabels.map((s) => s.replace("_", " ")),
          datasets: [
            {
              label: "Tasks by Status",
              data: statusData,
              backgroundColor: colors ?? defaultColors,
            },
          ],
        };
      }
      case "priority": {
        const priorityCounts = await this.prisma.task.groupBy({
          by: ["priority"],
          where,
          _count: { id: true },
        });

        const priorityLabels = Object.values(TaskPriority);
        const priorityData = priorityLabels.map((priority) => {
          const found = priorityCounts.find((p) => p.priority === priority);
          return found ? found._count.id : 0;
        });

        return {
          labels: priorityLabels,
          datasets: [
            {
              label: "Tasks by Priority",
              data: priorityData,
              backgroundColor: colors ?? ["#EF4444", "#F59E0B", "#3B82F6", "#10B981"],
            },
          ],
        };
      }
      case "project": {
        const projectCounts = await this.prisma.task.groupBy({
          by: ["projectId"],
          where: { ...where, projectId: { not: null } },
          _count: { id: true },
        });

        const projectIds = projectCounts.map((p) => {
          if (p.projectId === null) {
            throw new Error("Unexpected null projectId");
          }
          return p.projectId;
        });
        const projects = await this.prisma.project.findMany({
          where: { id: { in: projectIds } },
          select: { id: true, name: true, color: true },
        });

        return {
          labels: projects.map((p) => p.name),
          datasets: [
            {
              label: "Tasks by Project",
              data: projectCounts.map((p) => p._count.id),
              backgroundColor: projects.map((p) => p.color ?? "#3B82F6"),
            },
          ],
        };
      }
      default:
        return { labels: [], datasets: [] };
    }
  }

  private async getEventChartData(
    workspaceId: string,
    groupBy: string,
    filter?: Record<string, unknown>,
    colors?: string[]
  ): Promise<WidgetChartData> {
    const where: Record<string, unknown> = { workspaceId, ...filter };
    const defaultColors = ["#3B82F6", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6"];

    switch (groupBy) {
      case "project": {
        const projectCounts = await this.prisma.event.groupBy({
          by: ["projectId"],
          where: { ...where, projectId: { not: null } },
          _count: { id: true },
        });

        const projectIds = projectCounts.map((p) => {
          if (p.projectId === null) {
            throw new Error("Unexpected null projectId");
          }
          return p.projectId;
        });
        const projects = await this.prisma.project.findMany({
          where: { id: { in: projectIds } },
          select: { id: true, name: true, color: true },
        });

        return {
          labels: projects.map((p) => p.name),
          datasets: [
            {
              label: "Events by Project",
              data: projectCounts.map((p) => p._count.id),
              backgroundColor: projects.map((p) => p.color ?? "#3B82F6"),
            },
          ],
        };
      }
      default:
        return {
          labels: [],
          datasets: [{ label: "Events", data: [], backgroundColor: colors ?? defaultColors }],
        };
    }
  }

  private async getProjectChartData(
    workspaceId: string,
    groupBy: string,
    filter?: Record<string, unknown>,
    colors?: string[]
  ): Promise<WidgetChartData> {
    const where: Record<string, unknown> = { workspaceId, ...filter };
    const defaultColors = ["#3B82F6", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6"];

    switch (groupBy) {
      case "status": {
        const statusCounts = await this.prisma.project.groupBy({
          by: ["status"],
          where,
          _count: { id: true },
        });

        const statusLabels = Object.values(ProjectStatus);
        const statusData = statusLabels.map((status) => {
          const found = statusCounts.find((s) => s.status === status);
          return found ? found._count.id : 0;
        });

        return {
          labels: statusLabels.map((s) => s.replace("_", " ")),
          datasets: [
            {
              label: "Projects by Status",
              data: statusData,
              backgroundColor: colors ?? defaultColors,
            },
          ],
        };
      }
      default:
        return { labels: [], datasets: [] };
    }
  }

  private async getTaskListData(
    workspaceId: string,
    sortBy?: string,
    sortOrder?: "asc" | "desc",
    limit?: number,
    filter?: Record<string, unknown>
  ): Promise<WidgetListItem[]> {
    const where: Record<string, unknown> = { workspaceId, ...filter };
    const orderBy: Record<string, "asc" | "desc"> = {};

    if (sortBy) {
      orderBy[sortBy] = sortOrder ?? "desc";
    } else {
      orderBy.createdAt = "desc";
    }

    const tasks = await this.prisma.task.findMany({
      where,
      include: {
        project: { select: { name: true, color: true } },
      },
      orderBy,
      take: limit ?? 10,
    });

    return tasks.map((task) => {
      const item: WidgetListItem = {
        id: task.id,
        title: task.title,
        status: task.status,
        priority: task.priority,
      };
      if (task.project?.name) {
        item.subtitle = task.project.name;
      }
      if (task.dueDate) {
        item.dueDate = task.dueDate.toISOString();
      }
      if (task.project?.color) {
        item.color = task.project.color;
      }
      return item;
    });
  }

  private async getEventListData(
    workspaceId: string,
    sortBy?: string,
    sortOrder?: "asc" | "desc",
    limit?: number,
    filter?: Record<string, unknown>
  ): Promise<WidgetListItem[]> {
    const where: Record<string, unknown> = { workspaceId, ...filter };
    const orderBy: Record<string, "asc" | "desc"> = {};

    if (sortBy) {
      orderBy[sortBy] = sortOrder ?? "asc";
    } else {
      orderBy.startTime = "asc";
    }

    const events = await this.prisma.event.findMany({
      where,
      include: {
        project: { select: { name: true, color: true } },
      },
      orderBy,
      take: limit ?? 10,
    });

    return events.map((event) => {
      const item: WidgetListItem = {
        id: event.id,
        title: event.title,
        startTime: event.startTime.toISOString(),
      };
      if (event.project?.name) {
        item.subtitle = event.project.name;
      }
      if (event.project?.color) {
        item.color = event.project.color;
      }
      return item;
    });
  }

  private async getProjectListData(
    workspaceId: string,
    sortBy?: string,
    sortOrder?: "asc" | "desc",
    limit?: number,
    filter?: Record<string, unknown>
  ): Promise<WidgetListItem[]> {
    const where: Record<string, unknown> = { workspaceId, ...filter };
    const orderBy: Record<string, "asc" | "desc"> = {};

    if (sortBy) {
      orderBy[sortBy] = sortOrder ?? "desc";
    } else {
      orderBy.createdAt = "desc";
    }

    const projects = await this.prisma.project.findMany({
      where,
      orderBy,
      take: limit ?? 10,
    });

    return projects.map((project) => {
      const item: WidgetListItem = {
        id: project.id,
        title: project.name,
        status: project.status,
      };
      if (project.description) {
        item.subtitle = project.description;
      }
      if (project.color) {
        item.color = project.color;
      }
      return item;
    });
  }

  /**
   * Get active projects data
   */
  async getActiveProjectsData(workspaceId: string): Promise<WidgetProjectItem[]> {
    const projects = await this.prisma.project.findMany({
      where: {
        workspaceId,
        status: ProjectStatus.ACTIVE,
      },
      include: {
        _count: {
          select: { tasks: true, events: true },
        },
      },
      orderBy: {
        updatedAt: "desc",
      },
      take: 20,
    });

    return projects.map((project) => ({
      id: project.id,
      name: project.name,
      status: project.status,
      lastActivity: project.updatedAt.toISOString(),
      taskCount: project._count.tasks,
      eventCount: project._count.events,
      color: project.color,
    }));
  }

  /**
   * Get agent chains data (active agent sessions)
   */
  async getAgentChainsData(workspaceId: string): Promise<WidgetAgentSessionItem[]> {
    const sessions = await this.prisma.agentSession.findMany({
      where: {
        workspaceId,
        isActive: true,
      },
      include: {
        agent: {
          select: {
            name: true,
            status: true,
          },
        },
      },
      orderBy: {
        startedAt: "desc",
      },
      take: 20,
    });

    const now = new Date();

    return sessions.map((session) => ({
      id: session.id,
      sessionKey: session.sessionKey,
      label: session.label,
      channel: session.channel,
      agentName: session.agent?.name ?? null,
      agentStatus: session.agent?.status ?? null,
      status: session.isActive ? ("active" as const) : ("ended" as const),
      startedAt: session.startedAt.toISOString(),
      lastMessageAt: session.lastMessageAt ? session.lastMessageAt.toISOString() : null,
      runtimeMs: now.getTime() - session.startedAt.getTime(),
      messageCount: session.messageCount,
      contextSummary: session.contextSummary,
    }));
  }
}
