import { NextResponse } from "next/server";
import { DepartmentKey } from "@prisma/client";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { parseStringArray } from "@/lib/marketing/meetings";
import { canAccessProjects, canManageProjects, mapProjectMeeting } from "@/lib/projects/persistence";

type ParticipantInput = {
  userId?: string;
  displayName?: string;
};

type CreateMeetingRequestBody = {
  kind?: "request" | "schedule";
  type?: "meeting";
  title?: string;
  agenda?: string;
  notes?: string;
  participantNames?: unknown;
  participants?: unknown;
  suggestedTimes?: unknown;
  scheduledFor?: string;
  startAt?: string;
  endAt?: string;
};

function parseIsoDate(value: string | undefined): Date | null {
  if (!value) {
    return null;
  }

  const parsed = new Date(value);
  if (Number.isNaN(parsed.getTime())) {
    return null;
  }

  return parsed;
}

function parseParticipantInput(value: unknown): ParticipantInput[] {
  if (!Array.isArray(value)) {
    return [];
  }

  const parsed: ParticipantInput[] = [];

  for (const entry of value) {
    if (!entry || typeof entry !== "object") {
      continue;
    }

    const payload = entry as { userId?: unknown; displayName?: unknown };
    const userId = typeof payload.userId === "string" ? payload.userId.trim() : "";
    const displayName = typeof payload.displayName === "string" ? payload.displayName.trim() : "";

    if (!userId && !displayName) {
      continue;
    }

    const participant: ParticipantInput = {};
    if (userId) {
      participant.userId = userId;
    }

    if (displayName) {
      participant.displayName = displayName;
    }

    parsed.push(participant);
  }

  return parsed;
}

async function normalizeParticipants(
  inputParticipants: ParticipantInput[],
  participantNamesInput: string[]
): Promise<Array<{ userId: string | null; displayName: string }>> {
  const userIds = Array.from(new Set(inputParticipants.map((entry) => entry.userId).filter((value): value is string => Boolean(value))));
  const users =
    userIds.length > 0
      ? await prisma.user.findMany({
          where: {
            id: { in: userIds },
            status: "active",
          },
          select: {
            id: true,
            name: true,
            email: true,
          },
        })
      : [];

  const userMap = new Map(users.map((user) => [user.id, user]));
  const dedupe = new Set<string>();
  const participants: Array<{ userId: string | null; displayName: string }> = [];

  for (const entry of inputParticipants) {
    const normalizedUserId = entry.userId?.trim();
    const linkedUser = normalizedUserId ? userMap.get(normalizedUserId) : null;
    const displayName = linkedUser ? linkedUser.name?.trim() || linkedUser.email : entry.displayName?.trim() || "";

    if (!displayName) {
      continue;
    }

    const key = linkedUser ? `user:${linkedUser.id}` : `name:${displayName.toLowerCase()}`;
    if (dedupe.has(key)) {
      continue;
    }

    dedupe.add(key);
    participants.push({
      userId: linkedUser?.id ?? null,
      displayName,
    });
  }

  for (const displayName of participantNamesInput) {
    const cleanName = displayName.trim();
    if (!cleanName) {
      continue;
    }

    const key = `name:${cleanName.toLowerCase()}`;
    if (dedupe.has(key)) {
      continue;
    }

    dedupe.add(key);
    participants.push({
      userId: null,
      displayName: cleanName,
    });
  }

  return participants;
}

export async function GET() {
  const session = await getServerSession(authOptions);
  if (!session?.user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  if (!canAccessProjects(session.user.role, session.user.department)) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  const meetings = await prisma.marketingMeeting.findMany({
    where: {
      department: DepartmentKey.proyectos,
    },
    include: {
      commitments: {
        orderBy: { createdAt: "asc" },
      },
      participants: {
        orderBy: [{ responseStatus: "asc" }, { displayName: "asc" }],
      },
      calendarEvent: {
        select: { id: true },
      },
    },
    orderBy: [{ scheduledFor: "asc" }, { createdAt: "desc" }],
  });

  return NextResponse.json({
    ok: true,
    meetings: meetings.map(mapProjectMeeting),
  });
}

export async function POST(request: Request) {
  const session = await getServerSession(authOptions);
  if (!session?.user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  if (!canAccessProjects(session.user.role, session.user.department)) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  const body = (await request.json()) as CreateMeetingRequestBody;
  const title = body.title?.trim() ?? "";
  const agenda = body.agenda?.trim() || body.notes?.trim() || "";
  const participantNames = parseStringArray(body.participantNames);
  const participantInput = parseParticipantInput(body.participants);
  const normalizedParticipants = await normalizeParticipants(participantInput, participantNames);

  if (!agenda) {
    return NextResponse.json({ error: "Agenda is required." }, { status: 400 });
  }

  if (normalizedParticipants.length === 0) {
    return NextResponse.json({ error: "At least one participant is required." }, { status: 400 });
  }

  const suggestedTimes = parseStringArray(body.suggestedTimes);
  const mode = body.kind ?? (body.type === "meeting" ? "schedule" : "request");
  const isSchedule = mode === "schedule";

  if (!isSchedule && suggestedTimes.length === 0) {
    return NextResponse.json({ error: "At least one suggested time is required." }, { status: 400 });
  }

  const scheduledFor = parseIsoDate(body.startAt ?? body.scheduledFor);
  let endAt = parseIsoDate(body.endAt);

  if (isSchedule) {
    if (!canManageProjects(session.user.role, session.user.department)) {
      return NextResponse.json({ error: "Only owners or leaders can schedule meetings." }, { status: 403 });
    }

    if (!scheduledFor) {
      return NextResponse.json({ error: "startAt/scheduledFor is required for scheduled meetings." }, { status: 400 });
    }

    if (!endAt) {
      endAt = new Date(scheduledFor.getTime() + 60 * 60 * 1000);
    }

    if (endAt.getTime() <= scheduledFor.getTime()) {
      return NextResponse.json({ error: "endAt must be later than startAt." }, { status: 400 });
    }
  }

  const requestedByName = session.user.name?.trim() || session.user.email || "Usuario";

  const meeting = await prisma.marketingMeeting.create({
    data: {
      department: DepartmentKey.proyectos,
      title: title || (isSchedule ? "Reunión de proyectos" : "Solicitud de reunión"),
      agenda,
      status: isSchedule ? "scheduled" : "requested",
      requestedById: session.user.id,
      requestedByName,
      participantNames: normalizedParticipants.map((participant) => participant.displayName),
      suggestedTimes: isSchedule ? [] : suggestedTimes,
      scheduledFor: isSchedule ? scheduledFor : null,
      participants: {
        create: normalizedParticipants.map((participant) => ({
          userId: participant.userId,
          displayName: participant.displayName,
          responseStatus: participant.userId === session.user.id ? "accepted" : "pending",
          respondedAt: participant.userId === session.user.id ? new Date() : null,
        })),
      },
      ...(isSchedule && scheduledFor && endAt
        ? {
            calendarEvent: {
              create: {
                department: DepartmentKey.proyectos,
                ownerUserId: session.user.id,
                title: title || "Reunión de proyectos",
                notes: agenda,
                startAt: scheduledFor,
                endAt,
                visibility: "team",
              },
            },
          }
        : {}),
    },
    include: {
      commitments: {
        orderBy: { createdAt: "asc" },
      },
      participants: {
        orderBy: [{ responseStatus: "asc" }, { displayName: "asc" }],
      },
      calendarEvent: {
        select: { id: true },
      },
    },
  });

  return NextResponse.json({
    ok: true,
    meeting: mapProjectMeeting(meeting),
  });
}
