import { Prisma } from '@/generated/prisma/client';
import { db } from '@/lib/prisma';
import { parsePlanRules } from '@/modules/plan/services/plan-rules.service';
import { v7 as uuidv7 } from 'uuid';

export type CreateComplexInput = {
  complexName: string;
  physicalAddress: string;
  adminEmail: string;
  userId?: string;
  planCode?: string;
  city?: string;
  state?: string;
  country?: string;
  setupCourts?: boolean;
  courtSportId?: string;
  courtStartTime?: string;
  courtEndTime?: string;
  courtDaysOfWeek?: string[];
};

type DayOfWeek = 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY' | 'SUNDAY';

function toMinutes(value: string): number {
  const [hours, minutes] = value.split(':').map((part) => Number(part));
  return hours * 60 + minutes;
}

function assertAvailabilityRanges(
  availability: Array<{
    dayOfWeek: DayOfWeek;
    startTime: string;
    endTime: string;
  }>
) {
  for (const range of availability) {
    const start = toMinutes(range.startTime);
    const end = toMinutes(range.endTime);
    if (start >= end) {
      throw new Error(`El rango ${range.startTime}-${range.endTime} es inválido.`);
    }
  }
}

async function ensureActiveSport(sportId: string) {
  const sport = await db.sport.findFirst({
    where: { id: sportId, isActive: true },
    select: { id: true, name: true },
  });
  if (!sport) {
    throw new Error('El deporte seleccionado no existe o está inactivo.');
  }
  return sport;
}

export type UpdateComplexInput = {
  complexName?: string;
  physicalAddress?: string | null;
  complexSlug?: string;
  adminEmail?: string;
  planCode?: string | null;
  city?: string | null;
  state?: string | null;
  country?: string | null;
};

function slugify(value: string): string {
  return value
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\s-]/g, '')
    .replace(/\s+/g, '-')
    .replace(/-+/g, '-');
}

async function buildUniqueSlug(source: string, excludeComplexId?: string): Promise<string> {
  const base = slugify(source);
  const fallback = base.length > 0 ? base : `complex-${uuidv7().slice(0, 8)}`;

  let candidate = fallback;
  let index = 1;

  while (true) {
    const existing = await db.complex.findFirst({
      where: {
        complexSlug: candidate,
        ...(excludeComplexId
          ? {
              id: {
                not: excludeComplexId,
              },
            }
          : {}),
      },
      select: { id: true },
    });

    if (!existing) return candidate;

    index += 1;
    candidate = `${fallback}-${index}`;
  }
}

export async function createComplex(input: CreateComplexInput) {
  const { userId } = input;

  return db.$transaction(async (tx) => {
    const complexSlug = await buildUniqueSlug(input.complexName);

    const complex = await tx.complex.create({
      data: {
        id: uuidv7(),
        complexName: input.complexName,
        physicalAddress: input.physicalAddress.trim(),
        city: input.city?.trim() || null,
        state: input.state?.trim() || null,
        country: input.country?.trim() || null,
        complexSlug,
        adminEmail: input.adminEmail,
        planCode: input.planCode,
      },
    });

    if (userId) {
      await tx.complexUser.create({
        data: {
          complexId: complex.id,
          userId,
          role: 'ADMIN',
        },
      });
    }

    if (input.setupCourts && input.courtSportId) {
      const sport = await ensureActiveSport(input.courtSportId);
      const availability = (input.courtDaysOfWeek ?? []).map((day) => ({
        dayOfWeek: day as DayOfWeek,
        startTime: input.courtStartTime ?? '08:00',
        endTime: input.courtEndTime ?? '22:00',
      }));

      assertAvailabilityRanges(availability);

      const court = await tx.court.create({
        data: {
          id: uuidv7(),
          complexId: complex.id,
          sportId: input.courtSportId,
          name: `${sport.name} 1`,
          slotDurationMinutes: 60,
          basePrice: 0,
        },
      });

      await tx.courtAvailability.createMany({
        data: availability.map((avail) => ({
          id: uuidv7(),
          courtId: court.id,
          dayOfWeek: avail.dayOfWeek,
          startTime: avail.startTime,
          endTime: avail.endTime,
        })),
      });
    }

    return complex;
  });
}

export async function getComplexById(id: string) {
  return db.complex.findUnique({
    where: { id },
  });
}

export async function getComplexBySlug(slug: string) {
  return db.complex.findUnique({
    where: { complexSlug: slug },
  });
}

export async function listMyComplexes(userId: string) {
  const complexUsers = await db.complexUser.findMany({
    where: { userId },
    include: {
      complex: {
        include: {
          plan: true,
        },
      },
    },
    orderBy: {
      createdAt: 'asc',
    },
  });

  return complexUsers.map((complexUser) => ({
    ...complexUser.complex,
    role: complexUser.role,
    planFeatures: complexUser.complex.plan
      ? parsePlanRules(complexUser.complex.plan.rules).features
      : null,
  }));
}

export async function getCurrentComplex(userId: string, complexId: string) {
  const complexUser = await db.complexUser.findUnique({
    where: {
      complexId_userId: {
        complexId,
        userId,
      },
    },
    include: {
      complex: {
        include: {
          plan: true,
        },
      },
    },
  });

  if (!complexUser) return null;

  return {
    ...complexUser.complex,
    role: complexUser.role,
    planFeatures: complexUser.complex.plan
      ? parsePlanRules(complexUser.complex.plan.rules).features
      : null,
  };
}

export async function selectComplex(userId: string, complexId: string) {
  const complexUser = await db.complexUser.findUnique({
    where: {
      complexId_userId: {
        complexId,
        userId,
      },
    },
    include: {
      complex: {
        include: {
          plan: true,
        },
      },
    },
  });

  if (!complexUser) return null;

  return {
    ...complexUser.complex,
    role: complexUser.role,
    planFeatures: complexUser.complex.plan
      ? parsePlanRules(complexUser.complex.plan.rules).features
      : null,
  };
}

export async function updateComplex(id: string, input: UpdateComplexInput) {
  const data: Record<string, unknown> = {};

  if (input.complexName) {
    data.complexName = input.complexName;
    data.complexSlug = await buildUniqueSlug(input.complexName, id);
  }

  if (input.complexSlug) {
    data.complexSlug = await buildUniqueSlug(input.complexSlug, id);
  }

  if (input.adminEmail) {
    data.adminEmail = input.adminEmail;
  }

  if (input.planCode !== undefined) {
    data.planCode = input.planCode;
  }

  if (input.physicalAddress !== undefined) {
    data.physicalAddress = input.physicalAddress?.trim() ?? null;
  }

  if (input.city !== undefined) {
    data.city = input.city?.trim() ?? null;
  }

  if (input.state !== undefined) {
    data.state = input.state?.trim() ?? null;
  }

  if (input.country !== undefined) {
    data.country = input.country?.trim() ?? null;
  }

  return db.complex.update({
    where: { id },
    data: data as Prisma.ComplexUncheckedUpdateInput,
  });
}
