import { HttpStatus, Injectable } from '@nestjs/common';
import { Between } from 'typeorm';
import {
  assertToggle,
  resolveDataScope,
} from '../../common/access-control/permission-resolver.helpers';
import { PermissionUserContext } from '../../common/access-control/permission-context.types';
import { PermissionResolverService } from '../../common/access-control/permission-resolver.service';
import { OrgTimezoneService } from '../../common/timezone/org-timezone.service';
import { DatabaseService, ErrorResponse, logger } from '../../config';
import {
  createGeneralError,
  getCurrentMonthHolidays,
  getUtcMonthStartEnd,
} from '../../utils';
import { attendanceResponseDto } from '../attendance/attendance.dto';
import { holidayResponseDto } from '../holidays/holidays.dto';
import { noticeResponseDto } from '../notice/notice.dto';
import { monthHistoryResponseArraySchema } from './calendar.dto';

type CalendarHistoryItem = {
  type: 'holiday' | 'attendance' | 'notice';
  date: Date;
  title: string;
  description: string | null;
  department: string | null;
  status: string | null;
  checked_in: Date | null;
  checked_out: Date | null;
  start_date: Date | null;
  end_date: Date | null;
  image: any | null;
  total_working_hours: number | null;
};

@Injectable()
export class CalendarService {
  constructor(
    private readonly db: DatabaseService,
    private readonly permissionResolver: PermissionResolverService,
    private readonly orgTimezone: OrgTimezoneService,
  ) {}

  private async getPermissions(ctx: PermissionUserContext) {
    return this.permissionResolver.resolveForUser(ctx);
  }

  async getMonthHistory(
    owner_id: string,
    ctx: PermissionUserContext,
    month: number,
    year: number,
    dataScope?: 'own' | 'all',
  ) {
    try {
      const timeZone = await this.orgTimezone.getTimezone(owner_id);
      const permissions = await this.getPermissions(ctx);
      assertToggle(permissions, 'calendar.access');
      const scopeFilter = resolveDataScope(
        permissions,
        'calendar.view',
        dataScope,
        ctx,
      );
      const isMember = scopeFilter === 'own';
      const member_id = ctx.sub;

      const { startUtc, endUtc } = getUtcMonthStartEnd(year, month, 6, timeZone);
      const member = isMember
        ? await this.db.members.findUnique({
            where: {
              id: member_id,
              owner_id,
            },
            include: {
              department: true,
            },
          })
        : null;
      const asnDate = Between(startUtc, endUtc);
      const noticeWhere: any = {
        owner_id,
        published_at: asnDate,
      };

      const nonRecurringHolidayWhere: any = {
        owner_id,
        is_recurring: false,
        start_date: {
          lte: endUtc,
        },
        end_date: {
          gte: startUtc,
        },
      };

      const recurringHolidayWhere: any = {
        owner_id,
        is_recurring: true,
      };

      if (isMember && member) {
        const orConditions: any[] = [
          {
            recipients: {
              some: {
                id: member_id,
              },
            },
          },
        ];

        if (member.department_id) {
          orConditions.push({
            departments: {
              some: {
                id: member.department_id,
              },
            },
          });
          recurringHolidayWhere.departments = {
            some: {
              id: member.department_id,
            },
          };
          nonRecurringHolidayWhere.departments = {
            some: {
              id: member.department_id,
            },
          };
        }

        noticeWhere.OR = orConditions;
      }

      const [attendances, notices, nonRecurringHolidays, recurringHolidays] =
        await Promise.all([
          this.db.attendances.findMany({
            where: {
              owner_id,
              date: asnDate,
              ...(isMember ? { member_id } : {}),
            },
            orderBy: {
              date: 'desc',
            },
            include: {
              member: true,
            },
          }),
          this.db.notices.findMany({
            where: noticeWhere,
            include: {
              departments: true,
              recipients: true,
            },
            orderBy: {
              published_at: 'desc',
            },
          }),
          this.db.holidays.findMany({
            where: nonRecurringHolidayWhere,
            include: {
              departments: true,
            },
          }),
          this.db.holidays.findMany({
            where: recurringHolidayWhere,
            include: {
              departments: true,
            },
          }),
        ]);

      const holidays = getCurrentMonthHolidays(
        recurringHolidays,
        nonRecurringHolidays,
        startUtc,
        endUtc,
        timeZone,
      );

      const filteredNotices = notices?.map((item: noticeResponseDto) => ({
        id: item.id,
        title: item.title,
        content: item?.content && item.content !== 'NT/APL' ? item.content : null,
        date: item.published_at,
        start_date: item.published_at,
        end_date: item?.expire_at ?? null,
        departments: item?.departments || [],
        department: null,
        status: item.status,
        image: null,
        name: 'Notice',
        type: 'notice',
        attendance_type: '',
        priority: item?.priority ?? 'medium',
        check_in: null,
        check_out: null,
        break_hours: null,
        total_working_hours: null,
        lunch_start: null,
        lunch_end: null,
        breaks: [],
        note: '',
      }));

      const filteredHolidays = holidays?.map((item: holidayResponseDto) => ({
        id: item.id,
        title: item?.name ?? 'Holiday',
        content: item?.description ?? null,
        date: item?.start_date ?? null,
        start_date: item?.start_date ?? null,
        end_date: item?.end_date ?? null,
        departments: item?.departments || [],
        department: null,
        status: null,
        image: null,
        name: 'Holiday',
        type: 'holiday',
        attendance_type: '',
        check_in: null,
        check_out: null,
        break_hours: null,
        total_working_hours: null,
        lunch_start: null,
        lunch_end: null,
        breaks: [],
        note: '',
      }));

      const filteredAttendances = attendances?.map(
        (item: any) => ({
          id: item.id,
          title:
            item?.member?.first_name && item?.member?.last_name
              ? `${item.member.first_name} ${item.member.last_name}`
              : 'Attendance',
          content: item?.note && item.note !== 'NT/APL' ? item.note : null,
          date: item?.date ?? null,
          start_date: item?.date ?? null,
          end_date: item?.date ?? null,
          departments: [],
          department: item?.member?.department ?? null,
          status: item?.status ?? null,
          image: item?.member?.image ?? null,
          name: 'Attendance',
          type: 'attendance',
          attendance_type: item?.type ?? '',
          priority: null,
          check_in: item?.check_in ?? null,
          check_out: item?.check_out ?? null,
          break_hours: item?.break_hours ?? null,
          total_working_hours: item?.work_hours ?? null,
          lunch_start: null,
          lunch_end: null,
          breaks: [],
          note: item?.note && item.note !== 'NT/APL' ? item.note : '',
        }),
      );

      const history = [
        ...(filteredHolidays ?? []),
        ...(filteredAttendances ?? []),
        ...(filteredNotices ?? []),
      ];

      const { data: parsedData, error } =
        monthHistoryResponseArraySchema.safeParse(history);
      console.log('Parsed data:', parsedData, error);
      return {
        data: parsedData ?? [],
        message: 'Calendar month history retrieved successfully',
        status: HttpStatus.OK,
        details: {
          total: history.length,
          month,
          year,
          timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
        },
      };
    } catch (error) {
      logger.error(`Error in getSummary: ${String(error)}`);
      if (error instanceof ErrorResponse) throw error;
      throw createGeneralError(String(error), 'root', HttpStatus.BAD_REQUEST);
    }
  }
}
