import { UserRole } from '@prisma/client';

type ProfileLike = {
  estateId?: string | null;
  phone?: string | null;
  firstName?: string | null;
  lastName?: string | null;
  blockNumber?: string | null;
  flatNumber?: string | null;
  unit?: string | null;
  address?: string | null;
  guardCode?: string | null;
  assignedGateId?: string | null;
  assignedShifts?: unknown;
  profilePictureUrl?: string | null;
  profileConfirmed?: boolean | null;
  emergencyContact?: unknown;
};

export type ProfileCompletionResult = {
  completion: number;
  missing: string[];
  complete: boolean;
  required: string[];
};

export type ResidentProfileRequirement =
  | 'firstName'
  | 'lastName'
  | 'phone'
  | 'blockNumber'
  | 'flatNumber'
  | 'address'
  | 'estateId'
  | 'profileConfirmed';

export type GuardProfileRequirement =
  | 'firstName'
  | 'lastName'
  | 'phone'
  | 'guardCode'
  | 'assignedGateId'
  | 'assignedShifts'
  | 'profilePictureUrl'
  | 'address'
  | 'emergencyContact'
  | 'estateId'
  | 'profileConfirmed';

export type ProfileCompletionOptions = {
  residentRequiredFields?: ResidentProfileRequirement[];
  guardRequiredFields?: GuardProfileRequirement[];
};

const residentRequirementLabels: Record<ResidentProfileRequirement, string> = {
  firstName: 'First name',
  lastName: 'Last name',
  phone: 'Phone number',
  blockNumber: 'Block number',
  flatNumber: 'Flat number',
  address: 'Address',
  estateId: 'Estate assignment',
  profileConfirmed: 'Profile confirmation'
};

const guardRequirementLabels: Record<GuardProfileRequirement, string> = {
  firstName: 'First name',
  lastName: 'Last name',
  phone: 'Phone number',
  guardCode: 'Guard ID',
  assignedGateId: 'Assigned gate',
  assignedShifts: 'Assigned shifts',
  profilePictureUrl: 'Profile photo',
  address: 'Address',
  emergencyContact: 'Emergency contact',
  estateId: 'Estate assignment',
  profileConfirmed: 'Profile confirmation'
};

const defaultResidentRequiredFields: ResidentProfileRequirement[] = [
  'firstName',
  'lastName',
  'phone',
  'blockNumber',
  'flatNumber',
  'address',
  'estateId',
  'profileConfirmed'
];

const defaultGuardRequiredFields: GuardProfileRequirement[] = [
  'firstName',
  'lastName',
  'phone',
  'guardCode',
  'assignedGateId',
  'assignedShifts',
  'profilePictureUrl',
  'address',
  'emergencyContact',
  'estateId',
  'profileConfirmed'
];

export function calculateProfileCompletion(
  role: UserRole,
  profile: ProfileLike,
  options: ProfileCompletionOptions = {}
): ProfileCompletionResult {
  if (role === UserRole.RESIDENT) {
    return calculateRequiredCompletion(
      resolveResidentRequirements(profile, options.residentRequiredFields),
      residentRequirementLabels
    );
  }

  if (role === UserRole.GUARD) {
    return calculateRequiredCompletion(resolveGuardRequirements(profile, options.guardRequiredFields), guardRequirementLabels);
  }

  return { completion: 100, missing: [], required: [], complete: true };
}

function resolveResidentRequirements(
  profile: ProfileLike,
  fields = defaultResidentRequiredFields
): Array<[ResidentProfileRequirement, unknown]> {
  return fields.map((field) => {
    if (field === 'flatNumber') {
      return [field, profile.flatNumber || profile.unit];
    }
    if (field === 'profileConfirmed') {
      return [field, profile.profileConfirmed ? 'confirmed' : null];
    }
    return [field, profile[field]];
  });
}

function resolveGuardRequirements(
  profile: ProfileLike,
  fields = defaultGuardRequiredFields
): Array<[GuardProfileRequirement, unknown]> {
  return fields.map((field) => {
    if (field === 'assignedShifts') {
      return [field, hasListValue(profile.assignedShifts) ? 'assigned' : null];
    }
    if (field === 'emergencyContact') {
      return [field, hasObjectValue(profile.emergencyContact) ? 'provided' : null];
    }
    if (field === 'profileConfirmed') {
      return [field, profile.profileConfirmed ? 'confirmed' : null];
    }
    return [field, profile[field]];
  });
}

function calculateRequiredCompletion<T extends string>(
  items: Array<[T, unknown]>,
  labels: Record<T, string>
): ProfileCompletionResult {
  const required = items.map(([field]) => labels[field]);
  const missing = items.filter(([, value]) => !hasValue(value)).map(([field]) => labels[field]);
  const completion = Math.round(((items.length - missing.length) / items.length) * 100);
  return {
    completion,
    missing,
    required,
    complete: missing.length === 0
  };
}

function hasValue(value: unknown) {
  if (typeof value === 'string') {
    return value.trim().length > 0;
  }
  if (typeof value === 'boolean') {
    return value;
  }
  if (Array.isArray(value)) {
    return value.length > 0;
  }
  if (value && typeof value === 'object') {
    return Object.keys(value as Record<string, unknown>).length > 0;
  }
  return value !== null && value !== undefined;
}

function hasListValue(value: unknown) {
  return Array.isArray(value) ? value.length > 0 : hasValue(value);
}

function hasObjectValue(value: unknown) {
  return Boolean(value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value as Record<string, unknown>).length > 0);
}
