import { z } from 'zod';
import { IFile } from '../../config/types/interface';
import {
  dateTransformSchema,
  defaultTransformSchema,
} from '../shared/shared.dto';

const optionalString = z.preprocess((value) => {
  if (typeof value === 'string' && value.trim() === '') return undefined;
  return value;
}, z.string().trim().optional());

const optionalDate = z.preprocess((value) => {
  if (value === null || value === undefined || value === '') return undefined;
  return value;
}, z.coerce.date().optional());

const optionalBool = z.preprocess((value) => {
  if (value === null || value === undefined || value === '') return undefined;
  if (typeof value === 'boolean') return value;
  if (typeof value === 'string') {
    const normalized = value.trim().toLowerCase();
    if (normalized === 'true') return true;
    if (normalized === 'false') return false;
  }
  return value;
}, z.boolean().optional());

const optionalFileSchema = z
  .any()
  .transform((value) => {
    if (
      !value ||
      typeof value !== 'object' ||
      !('file_name' in value) ||
      typeof value.file_name !== 'string' ||
      value.file_name.trim() === ''
    ) {
      return undefined;
    }

    const host = process.env.SERVER_HOST ?? '';
    const hasHost = host.length > 0 && value.file_name.startsWith(host);

    return {
      ...value,
      file_name: hasHost ? value.file_name : `${host}/${value.file_name}`,
    };
  })
  .optional();

const noticeStatusSchema = z.enum(['published', 'draft', 'archived']);
const prioritySchema = z.enum(['low', 'medium', 'high', 'critical']);

// legacy DB statuses that existed before we introduced NoticeStatusEnum
const legacyStatusSchema = z.enum(['active', 'inactive', 'deleted']);

// Accept either the new notice status or legacy status and normalize to the new enum
const normalizedStatusSchema = z
  .union([noticeStatusSchema, legacyStatusSchema])
  .optional()
  .transform((val) => {
    if (val === undefined) return undefined;
    if (val === 'active') return 'published';
    if (val === 'inactive') return 'draft';
    if (val === 'deleted') return 'archived';
    return val as 'published' | 'draft' | 'archived';
  });

const departmentPreviewSchema = z.object({
  id: z.string().uuid(),
  name: z.string(),
  department_code: z.string(),
});

const memberPreviewSchema = z.object({
  id: z.string().uuid(),
  employee_id: z.string(),
  first_name: z.string(),
  last_name: defaultTransformSchema,
  nickname: defaultTransformSchema,
  image: z.any().optional(),
});

const targetTypeSchema = z.enum(['department', 'individual']);

const validateNoticeTargeting = (
  data: {
    target_type?: 'department' | 'individual';
    department_ids?: string[];
    member_ids?: string[];
  },
  ctx: z.RefinementCtx,
) => {
  const departmentIds = data.department_ids ?? [];
  const memberIds = data.member_ids ?? [];
  const targetType =
    data.target_type ??
    (memberIds.length > 0
      ? 'individual'
      : departmentIds.length > 0
        ? 'department'
        : undefined);

  if (!targetType) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      path: ['target_type'],
      message: 'Either department_ids or member_ids must be provided',
    });
    return;
  }

  if (targetType === 'department') {
    if (departmentIds.length === 0) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['department_ids'],
        message: 'At least one department is required',
      });
    }
    if (memberIds.length > 0) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['member_ids'],
        message: 'member_ids must be empty when targeting departments',
      });
    }
  }

  if (targetType === 'individual') {
    if (memberIds.length === 0) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['member_ids'],
        message: 'At least one member is required',
      });
    }
    if (departmentIds.length > 0) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['department_ids'],
        message: 'department_ids must be empty when targeting individuals',
      });
    }
  }
};

export const createNoticeSchema = z
  .object({
    title: z.string().trim().min(2, { message: 'Notice title is required' }),
    content: optionalString,
    document: optionalFileSchema,
    status: noticeStatusSchema.optional(),
    priority: prioritySchema.optional(),
    created_by: optionalString,
    published_at: optionalDate,
    expire_at: optionalDate,
    required_acknowledgment: optionalBool,
    target_type: targetTypeSchema.optional(),
    department_ids: z.array(z.string().uuid()).optional(),
    member_ids: z.array(z.string().uuid()).optional(),
  })
  .superRefine((data, ctx) => {
    validateNoticeTargeting(data, ctx);

    if (
      data.published_at &&
      data.expire_at &&
      data.expire_at < data.published_at
    ) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['expire_at'],
        message: 'Expire date must be greater than published date',
      });
    }
  });

export const updateNoticeSchema = z
  .object({
    title: optionalString,
    content: optionalString,
    document: optionalFileSchema.nullable(),
    status: noticeStatusSchema.optional(),
    priority: prioritySchema.optional(),
    created_by: optionalString,
    published_at: optionalDate,
    expire_at: optionalDate,
    required_acknowledgment: optionalBool,
    target_type: targetTypeSchema.optional(),
    department_ids: z.array(z.string().uuid()).optional(),
    member_ids: z.array(z.string().uuid()).optional(),
  })
  .refine((data) => Object.keys(data).length > 0, {
    message: 'At least one field must be provided',
  })
  .superRefine((data, ctx) => {
    if (
      data.target_type !== undefined ||
      data.department_ids !== undefined ||
      data.member_ids !== undefined
    ) {
      validateNoticeTargeting(
        {
          target_type: data.target_type,
          department_ids: data.department_ids ?? [],
          member_ids: data.member_ids ?? [],
        },
        ctx,
      );
    }

    if (
      data.published_at &&
      data.expire_at &&
      data.expire_at < data.published_at
    ) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['expire_at'],
        message: 'Expire date must be greater than published date',
      });
    }
  });

export const UpdateNoticeAcknowledgmentSchema = z.object({
  member_id: z.string().uuid(),
  acknowledgment: z.enum(['up', 'down']),
});

export const noticeFilterSchema = z.object({
  status: normalizedStatusSchema,
  priority: prioritySchema.optional(),
  required_acknowledgment: optionalBool,
  from: optionalDate,
  to: optionalDate,
  department_id: z.preprocess((value) => {
    if (typeof value === 'string' && value.trim() === '') return undefined;
    return value;
  }, z.string().uuid().optional()),
  search: z.string().trim().optional(),
  page: z.preprocess((value) => {
    if (value === undefined || value === null || value === '') return 1;
    const parsed = Number(value);
    return Number.isNaN(parsed) ? value : parsed;
  }, z.number().int().min(1).default(1)),
  limit: z.preprocess((value) => {
    if (value === undefined || value === null || value === '') return 10;
    const parsed = Number(value);
    return Number.isNaN(parsed) ? value : parsed;
  }, z.number().int().min(1).max(100).default(10)),
  data_scope: z.enum(['own', 'all']).optional(),
});

export const noticeResponseSchema = z.object({
  id: z.string().uuid().optional(),
  title: z.string().optional(),
  content: defaultTransformSchema,
  document: z
    .any()
    .transform((val: IFile) => {
      if (!val || !val?.file_name) return null;
      const isHostIncluded = val?.file_name?.includes(process.env.SERVER_HOST);
      return {
        ...val,
        file_name: isHostIncluded
          ? val?.file_name
          : `${process.env.SERVER_HOST}/${val?.file_name}`,
      };
    })
    .optional(),
  status: normalizedStatusSchema,
  priority: prioritySchema.optional(),
  created_by: defaultTransformSchema,
  published_at: dateTransformSchema.nullable(),
  expire_at: dateTransformSchema.nullable(),
  required_acknowledgment: z.boolean().optional(),
  target_type: targetTypeSchema.optional(),
  departments: z.array(departmentPreviewSchema).optional(),
  recipients: z.array(memberPreviewSchema).optional(),
  up_acknowledgment: z.array(memberPreviewSchema).optional(),
  down_acknowledgment: z.array(memberPreviewSchema).optional(),
  created_at: dateTransformSchema,
  updated_at: dateTransformSchema,
});

export const noticesResponseSchema = z.array(noticeResponseSchema);

export type createNoticeDto = z.infer<typeof createNoticeSchema>;
export type updateNoticeDto = z.infer<typeof updateNoticeSchema>;
export type noticeFilterDto = z.infer<typeof noticeFilterSchema>;
export type noticeResponseDto = z.infer<typeof noticeResponseSchema>;
export type noticesResponseDto = z.infer<typeof noticesResponseSchema>;
export type updateNoticeAcknowledgmentDto = z.infer<
  typeof UpdateNoticeAcknowledgmentSchema
>;
