'use server';

import { createClient } from '@/app/_utils/supabase/client';

import {
  ICrimes,
  ICrimesByYearAndMonth,
  IDistanceResult,
} from '@/app/_utils/types/crimes';
import { getInjection } from '@/di/container';
import db from '@/prisma/db';
import {
  AuthenticationError,
  UnauthenticatedError,
} from '@/src/entities/errors/auth';
import { InputParseError } from '@/src/entities/errors/common';

export async function getAvailableYears() {
  const instrumentationService = getInjection('IInstrumentationService');
  return await instrumentationService.instrumentServerAction(
    'Available Years',
    { recordResponse: true },
    async () => {
      try {
        const years = await db.crimes.findMany({
          select: {
            year: true,
          },
          distinct: ['year'],
          orderBy: {
            year: 'asc',
          },
        });

        return years.map((year) => year.year);
      } catch (err) {
        if (err instanceof InputParseError) {
          // return {
          //   error: err.message,
          // };

          throw new InputParseError(err.message);
        }

        if (err instanceof AuthenticationError) {
          // return {
          //   error: 'User not found.',
          // };

          throw new AuthenticationError(
            'There was an error with the credentials. Please try again or contact support.'
          );
        }

        const crashReporterService = getInjection('ICrashReporterService');
        crashReporterService.report(err);
        // return {
        //   error:
        //     'An error happened. The developers have been notified. Please try again later.',
        // };
        throw new Error(
          'An error happened. The developers have been notified. Please try again later.'
        );
      }
    }
  );
}

export async function getCrimeCategories() {
  const instrumentationService = getInjection('IInstrumentationService');
  return await instrumentationService.instrumentServerAction(
    'Crime Categories',
    { recordResponse: true },
    async () => {
      try {
        const categories = await db.crime_categories.findMany({
          select: {
            id: true,
            name: true,
            type: true,
          },
        });

        return categories;
      } catch (err) {
        if (err instanceof InputParseError) {
          // return {
          //   error: err.message,
          // };

          throw new InputParseError(err.message);
        }

        if (err instanceof AuthenticationError) {
          // return {
          //   error: 'User not found.',
          // };

          throw new AuthenticationError(
            'There was an error with the credentials. Please try again or contact support.'
          );
        }

        const crashReporterService = getInjection('ICrashReporterService');
        crashReporterService.report(err);
        // return {
        //   error:
        //     'An error happened. The developers have been notified. Please try again later.',
        // };
        throw new Error(
          'An error happened. The developers have been notified. Please try again later.'
        );
      }
    }
  );
}

export async function getCrimes(): Promise<ICrimes[]> {
  const instrumentationService = getInjection('IInstrumentationService');
  return await instrumentationService.instrumentServerAction(
    'District Crime Data',
    { recordResponse: true },
    async () => {
      try {
        const crimes = await db.crimes.findMany({
          include: {
            districts: {
              select: {
                name: true,
                geographics: {
                  select: {
                    address: true,
                    land_area: true,
                    year: true,
                    latitude: true,
                    longitude: true,
                  },
                },
                demographics: {
                  select: {
                    number_of_unemployed: true,
                    population: true,
                    population_density: true,
                    year: true,
                  },
                },
              },
            },
            crime_incidents: {
              select: {
                id: true,
                timestamp: true,
                description: true,
                status: true,
                crime_categories: {
                  select: {
                    name: true,
                    type: true,
                  },
                },
                locations: {
                  select: {
                    address: true,
                    latitude: true,
                    longitude: true,
                  },
                },
              },
            },
          },
        });

        return crimes;
      } catch (err) {
        if (err instanceof InputParseError) {
          // return {
          //   error: err.message,
          // };

          throw new InputParseError(err.message);
        }

        if (err instanceof AuthenticationError) {
          // return {
          //   error: 'User not found.',
          // };

          throw new AuthenticationError(
            'There was an error with the credentials. Please try again or contact support.'
          );
        }

        const crashReporterService = getInjection('ICrashReporterService');
        crashReporterService.report(err);
        // return {
        //   error:
        //     'An error happened. The developers have been notified. Please try again later.',
        // };
        throw new Error(
          'An error happened. The developers have been notified. Please try again later.'
        );
      }
    }
  );
}

export async function getCrimeByYearAndMonth(
  year: number,
  month: number | 'all'
): Promise<ICrimesByYearAndMonth[]> {
  const instrumentationService = getInjection('IInstrumentationService');
  return await instrumentationService.instrumentServerAction(
    'District Crime Data',
    { recordResponse: true },
    async () => {
      try {
        // Build where clause conditionally based on provided parameters
        const whereClause: any = {
          year: year, // Always filter by year now since "all" is removed
        };

        // Only add month to filter if it's not "all"
        if (month !== 'all') {
          whereClause.month = month;
        }

        const crimes = await db.crimes.findMany({
          where: whereClause,
          include: {
            districts: {
              select: {
                name: true,
                geographics: {
                  where: { year }, // Match geographics to selected year
                  select: {
                    address: true,
                    land_area: true,
                    year: true,
                    latitude: true,
                    longitude: true,
                  },
                },
                demographics: {
                  where: { year }, // Match demographics to selected year
                  select: {
                    number_of_unemployed: true,
                    population: true,
                    population_density: true,
                    year: true,
                  },
                },
              },
            },
            crime_incidents: {
              select: {
                id: true,
                timestamp: true,
                description: true,
                status: true,
                crime_categories: {
                  select: {
                    name: true,
                    type: true,
                  },
                },
                locations: {
                  select: {
                    address: true,
                    latitude: true,
                    longitude: true,
                  },
                },
              },
            },
          },
        });

        // Process the data to transform geographics and demographics from array to single object
        const processedCrimes = crimes.map((crime) => {
          return {
            ...crime,
            districts: {
              ...crime.districts,
              // Convert geographics array to single object matching the year
              geographics: crime.districts.geographics[0] || null,
              // Convert demographics array to single object matching the year
              demographics: crime.districts.demographics[0] || null,
            },
          };
        });

        return processedCrimes;
      } catch (err) {
        if (err instanceof InputParseError) {
          throw new InputParseError(err.message);
        }

        if (err instanceof AuthenticationError) {
          throw new AuthenticationError(
            'There was an error with the credentials. Please try again or contact support.'
          );
        }

        const crashReporterService = getInjection('ICrashReporterService');
        crashReporterService.report(err);
        throw new Error(
          'An error happened. The developers have been notified. Please try again later.'
        );
      }
    }
  );
}

/**
 * Calculate distances between units and incidents using PostGIS
 * @param unitId Optional unit code to filter by specific unit
 * @param districtId Optional district ID to filter by specific district
 * @returns Array of distance calculations between units and incidents
 */
export async function calculateDistances(
  unitId?: string,
  districtId?: string
): Promise<IDistanceResult[]> {
  const supabase = createClient();

  try {
    const { data, error } = await supabase.rpc(
      'calculate_unit_incident_distances',
      {
        unit_id: unitId || null,
        district_id: districtId || null,
      }
    );

    if (error) {
      console.error('Error calculating distances:', error);
      return [];
    }

    return data || [];
  } catch (error) {
    console.error('Failed to calculate distances:', error);
    return [];
  }
}


