/** Stable API error codes — frontend maps these to translated user messages. */
export const ErrorCode = {
  // Auth
  AUTH_INVALID_CREDENTIALS: 'AUTH_INVALID_CREDENTIALS',
  AUTH_UNAUTHORIZED: 'AUTH_UNAUTHORIZED',
  AUTH_REFRESH_TOKEN_NOT_FOUND: 'AUTH_REFRESH_TOKEN_NOT_FOUND',
  AUTH_REFRESH_TOKEN_INVALID: 'AUTH_REFRESH_TOKEN_INVALID',
  AUTH_SESSION_EXPIRED: 'AUTH_SESSION_EXPIRED',
  AUTH_USER_NOT_FOUND: 'AUTH_USER_NOT_FOUND',
  AUTH_ORG_NOT_SELECTED: 'AUTH_ORG_NOT_SELECTED',
  AUTH_ORG_ACCESS_DENIED: 'AUTH_ORG_ACCESS_DENIED',
  AUTH_INVITATION_PENDING: 'AUTH_INVITATION_PENDING',
  AUTH_REGISTRATION_EMAIL_EXISTS: 'AUTH_REGISTRATION_EMAIL_EXISTS',
  AUTH_REGISTRATION_MOBILE_EXISTS: 'AUTH_REGISTRATION_MOBILE_EXISTS',
  AUTH_REGISTRATION_MOBILE_INVALID: 'AUTH_REGISTRATION_MOBILE_INVALID',
  AUTH_REGISTRATION_FAILED: 'AUTH_REGISTRATION_FAILED',
  AUTH_PASSWORD_INCORRECT: 'AUTH_PASSWORD_INCORRECT',
  AUTH_PASSWORD_RESET_EXPIRED: 'AUTH_PASSWORD_RESET_EXPIRED',
  AUTH_VERIFICATION_CODE_INVALID: 'AUTH_VERIFICATION_CODE_INVALID',
  AUTH_VERIFICATION_RATE_LIMIT: 'AUTH_VERIFICATION_RATE_LIMIT',
  AUTH_SELECT_ORG_FIRST: 'AUTH_SELECT_ORG_FIRST',
  AUTH_CREATE_ORG_OWNER_ONLY: 'AUTH_CREATE_ORG_OWNER_ONLY',
  AUTH_PASSWORD_CURRENT_REQUIRED: 'AUTH_PASSWORD_CURRENT_REQUIRED',

  // Permission
  PERMISSION_DENIED: 'PERMISSION_DENIED',
  PERMISSION_ORG_MANAGE: 'PERMISSION_ORG_MANAGE',
  PERMISSION_CLINIC_ONLY: 'PERMISSION_CLINIC_ONLY',
  PERMISSION_LAB_ONLY: 'PERMISSION_LAB_ONLY',
  PERMISSION_NOT_MEMBER: 'PERMISSION_NOT_MEMBER',
  PERMISSION_OWNER_ONLY: 'PERMISSION_OWNER_ONLY',
  PERMISSION_PARTICIPATION_SUBSCRIPTION:
    'PERMISSION_PARTICIPATION_SUBSCRIPTION',
  PERMISSION_ENABLE_PARTICIPATION_FIRST:
    'PERMISSION_ENABLE_PARTICIPATION_FIRST',
  PERMISSION_CLINIC_WORKING_HOURS: 'PERMISSION_CLINIC_WORKING_HOURS',
  PERMISSION_ACCESS_APPOINTMENTS: 'PERMISSION_ACCESS_APPOINTMENTS',
  PERMISSION_EDIT_APPOINTMENTS: 'PERMISSION_EDIT_APPOINTMENTS',
  PERMISSION_ACCESS_TREATMENTS: 'PERMISSION_ACCESS_TREATMENTS',
  PERMISSION_EDIT_TREATMENTS: 'PERMISSION_EDIT_TREATMENTS',
  PERMISSION_ACCESS_TASKS: 'PERMISSION_ACCESS_TASKS',
  PERMISSION_EDIT_TASKS: 'PERMISSION_EDIT_TASKS',
  PERMISSION_ACCESS_CASES: 'PERMISSION_ACCESS_CASES',
  PERMISSION_ACCESS_STAFF: 'PERMISSION_ACCESS_STAFF',
  PERMISSION_EDIT_STAFF: 'PERMISSION_EDIT_STAFF',
  PERMISSION_ORG_NOT_FOUND: 'PERMISSION_ORG_NOT_FOUND',
  LAB_CASE_ACCESS_DENIED: 'LAB_CASE_ACCESS_DENIED',

  // Validation
  VALIDATION_FAILED: 'VALIDATION_FAILED',
  VALIDATION_EMAIL_INVALID: 'VALIDATION_EMAIL_INVALID',
  VALIDATION_PASSWORD_TOO_SHORT: 'VALIDATION_PASSWORD_TOO_SHORT',
  VALIDATION_PASSWORD_REQUIRED: 'VALIDATION_PASSWORD_REQUIRED',
  VALIDATION_MOBILE_INVALID: 'VALIDATION_MOBILE_INVALID',
  VALIDATION_NAME_TOO_SHORT: 'VALIDATION_NAME_TOO_SHORT',
  VALIDATION_ORGANIZATION_NAME_REQUIRED:
    'VALIDATION_ORGANIZATION_NAME_REQUIRED',
  VALIDATION_ORGANIZATION_TYPE_INVALID: 'VALIDATION_ORGANIZATION_TYPE_INVALID',
  VALIDATION_TOKEN_REQUIRED: 'VALIDATION_TOKEN_REQUIRED',
  VALIDATION_FIELD_REQUIRED: 'VALIDATION_FIELD_REQUIRED',
  VALIDATION_LANGUAGE_INVALID: 'VALIDATION_LANGUAGE_INVALID',
  VALIDATION_INVALID_REQUEST: 'VALIDATION_INVALID_REQUEST',
  VALIDATION_TIMEZONE_INVALID: 'VALIDATION_TIMEZONE_INVALID',

  // Appointments
  APPOINTMENT_INVALID_TIME: 'APPOINTMENT_INVALID_TIME',
  APPOINTMENT_END_BEFORE_START: 'APPOINTMENT_END_BEFORE_START',
  APPOINTMENT_TOO_LONG: 'APPOINTMENT_TOO_LONG',
  APPOINTMENT_IN_PAST: 'APPOINTMENT_IN_PAST',
  APPOINTMENT_INVALID_RANGE: 'APPOINTMENT_INVALID_RANGE',
  APPOINTMENT_INVALID_RANGE_ORDER: 'APPOINTMENT_INVALID_RANGE_ORDER',
  APPOINTMENT_INVALID_DATE: 'APPOINTMENT_INVALID_DATE',
  APPOINTMENT_PROVIDER_NOT_MEMBER: 'APPOINTMENT_PROVIDER_NOT_MEMBER',
  APPOINTMENT_PROVIDER_INACTIVE: 'APPOINTMENT_PROVIDER_INACTIVE',
  APPOINTMENT_PROVIDER_NO_TREATMENT_EDIT:
    'APPOINTMENT_PROVIDER_NO_TREATMENT_EDIT',
  APPOINTMENT_PROVIDER_NO_WORKING_HOURS:
    'APPOINTMENT_PROVIDER_NO_WORKING_HOURS',
  APPOINTMENT_PROVIDER_NOT_WORKING_DAY: 'APPOINTMENT_PROVIDER_NOT_WORKING_DAY',
  APPOINTMENT_OUTSIDE_WORKING_HOURS: 'APPOINTMENT_OUTSIDE_WORKING_HOURS',
  APPOINTMENT_NOT_FOUND: 'APPOINTMENT_NOT_FOUND',
  APPOINTMENT_PATIENT_LOCKED: 'APPOINTMENT_PATIENT_LOCKED',
  APPOINTMENT_HAS_TREATMENT: 'APPOINTMENT_HAS_TREATMENT',
  APPOINTMENT_NOT_PROVIDER: 'APPOINTMENT_NOT_PROVIDER',

  PATIENT_NOT_FOUND: 'PATIENT_NOT_FOUND',
  PATIENT_MOBILE_UNAVAILABLE: 'PATIENT_MOBILE_UNAVAILABLE',

  WORKING_HOURS_INVALID: 'WORKING_HOURS_INVALID',
  WORKING_HOURS_OWNER_NOT_ALLOWED: 'WORKING_HOURS_OWNER_NOT_ALLOWED',
  WORKING_HOURS_CONFLICTS_WITH_APPOINTMENTS:
    'WORKING_HOURS_CONFLICTS_WITH_APPOINTMENTS',

  STAFF_UNKNOWN_PERMISSIONS: 'STAFF_UNKNOWN_PERMISSIONS',
  STAFF_NO_SUBSCRIPTION: 'STAFF_NO_SUBSCRIPTION',
  STAFF_SEAT_LIMIT: 'STAFF_SEAT_LIMIT',
  STAFF_INVITE_OWNER_EMAIL: 'STAFF_INVITE_OWNER_EMAIL',
  STAFF_ALREADY_MEMBER: 'STAFF_ALREADY_MEMBER',
  STAFF_OWNER_NO_INVITE_LINK: 'STAFF_OWNER_NO_INVITE_LINK',
  STAFF_ALREADY_ACCEPTED: 'STAFF_ALREADY_ACCEPTED',
  STAFF_INVITE_MISSING: 'STAFF_INVITE_MISSING',
  STAFF_INVITE_ALREADY_ACCEPTED: 'STAFF_INVITE_ALREADY_ACCEPTED',
  STAFF_INVITE_INVALID: 'STAFF_INVITE_INVALID',
  STAFF_INVITE_REVOKED: 'STAFF_INVITE_REVOKED',
  STAFF_INVITE_EXPIRED: 'STAFF_INVITE_EXPIRED',
  STAFF_ALREADY_ACTIVE: 'STAFF_ALREADY_ACTIVE',
  STAFF_ALREADY_DISABLED: 'STAFF_ALREADY_DISABLED',
  STAFF_CANNOT_DISABLE_SELF: 'STAFF_CANNOT_DISABLE_SELF',
  STAFF_MEMBER_NOT_FOUND: 'STAFF_MEMBER_NOT_FOUND',
  STAFF_CANNOT_EDIT_OWNER: 'STAFF_CANNOT_EDIT_OWNER',
  STAFF_CANNOT_ENABLE_OWNER: 'STAFF_CANNOT_ENABLE_OWNER',
  STAFF_CANNOT_DISABLE_OWNER: 'STAFF_CANNOT_DISABLE_OWNER',
  STAFF_CANNOT_REMOVE_OWNER: 'STAFF_CANNOT_REMOVE_OWNER',
  STAFF_CANNOT_CLEAR_OWN_PASSWORD: 'STAFF_CANNOT_CLEAR_OWN_PASSWORD',
  STAFF_PASSWORD_CLEAR_ACTIVE_ONLY: 'STAFF_PASSWORD_CLEAR_ACTIVE_ONLY',

  ORG_CANNOT_LINK_SELF: 'ORG_CANNOT_LINK_SELF',
  ORG_LINK_WRONG_TYPE: 'ORG_LINK_WRONG_TYPE',
  ORG_TARGET_NO_SUBSCRIPTION: 'ORG_TARGET_NO_SUBSCRIPTION',
  ORG_LINK_EXISTS: 'ORG_LINK_EXISTS',
  ORG_REQUEST_NOT_PENDING: 'ORG_REQUEST_NOT_PENDING',
  ORG_CANNOT_RESPOND_OWN: 'ORG_CANNOT_RESPOND_OWN',
  ORG_INVITE_ALREADY_ACCEPTED: 'ORG_INVITE_ALREADY_ACCEPTED',
  ORG_INVITE_INVALID: 'ORG_INVITE_INVALID',
  ORG_INVITE_REVOKED: 'ORG_INVITE_REVOKED',
  ORG_INVITE_EXPIRED: 'ORG_INVITE_EXPIRED',
  ORG_INVITE_TYPE_MISMATCH: 'ORG_INVITE_TYPE_MISMATCH',
  ORG_INVITE_OWNER_HAS_ORG: 'ORG_INVITE_OWNER_HAS_ORG',
  ORG_UNKNOWN_TYPE: 'ORG_UNKNOWN_TYPE',
  ORG_COUNTERPART_NOT_LAB: 'ORG_COUNTERPART_NOT_LAB',
  ORG_COUNTERPART_NOT_CLINIC: 'ORG_COUNTERPART_NOT_CLINIC',
  ORG_CONNECTION_NOT_FOUND: 'ORG_CONNECTION_NOT_FOUND',
  ORG_INVITE_NOT_FOUND: 'ORG_INVITE_NOT_FOUND',
  ORG_ONLY_CLINIC_COMMENT: 'ORG_ONLY_CLINIC_COMMENT',

  CATALOG_UNKNOWN_TREATMENT_TYPE: 'CATALOG_UNKNOWN_TREATMENT_TYPE',
  CATALOG_UNKNOWN_PROSTHESIS_TYPE: 'CATALOG_UNKNOWN_PROSTHESIS_TYPE',
  CATALOG_TREATMENT_NOT_LAB_DEPENDENT: 'CATALOG_TREATMENT_NOT_LAB_DEPENDENT',

  TREATMENT_SAVE_DETAILS_BEFORE_LAB: 'TREATMENT_SAVE_DETAILS_BEFORE_LAB',
  TREATMENT_DETAIL_ONE_CASE: 'TREATMENT_DETAIL_ONE_CASE',
  TREATMENT_DETAILS_NOT_FOUND: 'TREATMENT_DETAILS_NOT_FOUND',
  TREATMENT_DEST_NOT_LINKED: 'TREATMENT_DEST_NOT_LINKED',
  TREATMENT_TEETH_REQUIRED_TO_SHIP: 'TREATMENT_TEETH_REQUIRED_TO_SHIP',
  TREATMENT_TOOTH_UNKNOWN_DETAIL: 'TREATMENT_TOOTH_UNKNOWN_DETAIL',
  TREATMENT_TOOTH_NOT_ON_DETAIL: 'TREATMENT_TOOTH_NOT_ON_DETAIL',
  TREATMENT_CASE_NO_DEST: 'TREATMENT_CASE_NO_DEST',
  TREATMENT_CASE_NEEDS_DETAIL: 'TREATMENT_CASE_NEEDS_DETAIL',
  TREATMENT_CASE_ONE_DETAIL: 'TREATMENT_CASE_ONE_DETAIL',
  TREATMENT_ONLY_PROVIDER_SEND: 'TREATMENT_ONLY_PROVIDER_SEND',
  TREATMENT_CASE_ALREADY_SENT: 'TREATMENT_CASE_ALREADY_SENT',
  TREATMENT_DUE_DATE_LOCKED: 'TREATMENT_DUE_DATE_LOCKED',
  TREATMENT_INVALID_DUE_DATE: 'TREATMENT_INVALID_DUE_DATE',
  TREATMENT_DETAIL_KEY_REQUIRED: 'TREATMENT_DETAIL_KEY_REQUIRED',
  TREATMENT_FILE_REQUIRED: 'TREATMENT_FILE_REQUIRED',
  TREATMENT_ATTACHMENT_NOT_FOUND: 'TREATMENT_ATTACHMENT_NOT_FOUND',
  TREATMENT_FILE_UNAVAILABLE: 'TREATMENT_FILE_UNAVAILABLE',
  TREATMENT_CASE_INVALID_ATTACHMENTS: 'TREATMENT_CASE_INVALID_ATTACHMENTS',
  TREATMENT_TOOTH_PROSTHESIS_INCOMPLETE:
    'TREATMENT_TOOTH_PROSTHESIS_INCOMPLETE',
  TREATMENT_DETAIL_SENT: 'TREATMENT_DETAIL_SENT',
  TREATMENT_NOT_FOUND: 'TREATMENT_NOT_FOUND',
  TREATMENT_PATIENT_OR_WALK_IN: 'TREATMENT_PATIENT_OR_WALK_IN',
  TREATMENT_NOT_STANDALONE: 'TREATMENT_NOT_STANDALONE',
  TREATMENT_HAS_DETAILS: 'TREATMENT_HAS_DETAILS',
  LAB_CASE_NOT_FOUND: 'LAB_CASE_NOT_FOUND',
  LAB_CASE_NOT_EDITABLE: 'LAB_CASE_NOT_EDITABLE',
  LAB_CASE_ALREADY_STARTED: 'LAB_CASE_ALREADY_STARTED',
  LAB_CASE_LINES_REQUIRED: 'LAB_CASE_LINES_REQUIRED',
  LAB_CASE_START_INCOMPLETE: 'LAB_CASE_START_INCOMPLETE',
  LAB_CASE_CLIENT_REQUIRED: 'LAB_CASE_CLIENT_REQUIRED',

  TASK_ASSIGNEE_INVALID: 'TASK_ASSIGNEE_INVALID',
  TASK_ASSIGNED_TO_OTHER: 'TASK_ASSIGNED_TO_OTHER',
  TASK_PAGE_DATE_SORT_ONLY: 'TASK_PAGE_DATE_SORT_ONLY',
  INVALID_SENT_FROM: 'INVALID_SENT_FROM',
  INVALID_SENT_TO: 'INVALID_SENT_TO',

  CASE_NOT_FOUND: 'CASE_NOT_FOUND',
  CASE_ATTACHMENT_NOT_FOUND: 'CASE_ATTACHMENT_NOT_FOUND',
  CASE_ATTACHMENT_MISSING_FILE: 'CASE_ATTACHMENT_MISSING_FILE',
  CASE_TASK_NOT_FOUND: 'CASE_TASK_NOT_FOUND',

  COMMENT_NOT_FOUND: 'COMMENT_NOT_FOUND',
  COMMENT_VISIBILITY_LAB_ONLY: 'COMMENT_VISIBILITY_LAB_ONLY',

  TODAY_INVALID_RANGE: 'TODAY_INVALID_RANGE',
  TODAY_INVALID_RANGE_ORDER: 'TODAY_INVALID_RANGE_ORDER',

  // Voice treatment entry
  VOICE_NOT_AVAILABLE: 'VOICE_NOT_AVAILABLE',
  VOICE_CLIP_TOO_LONG: 'VOICE_CLIP_TOO_LONG',
  VOICE_UNSUPPORTED_FORMAT: 'VOICE_UNSUPPORTED_FORMAT',
  VOICE_ASR_FAILED: 'VOICE_ASR_FAILED',
  VOICE_EXTRACT_FAILED: 'VOICE_EXTRACT_FAILED',
  VOICE_NOTHING_RECOGNIZED: 'VOICE_NOTHING_RECOGNIZED',
  VOICE_RATE_LIMITED: 'VOICE_RATE_LIMITED',

  // Generic HTTP
  NOT_FOUND: 'NOT_FOUND',
  CONFLICT: 'CONFLICT',
  CONFLICT_FUTURE_APPOINTMENTS: 'CONFLICT_FUTURE_APPOINTMENTS',
  BAD_REQUEST: 'BAD_REQUEST',
  INTERNAL_ERROR: 'INTERNAL_ERROR',
} as const;

export type ErrorCodeValue = (typeof ErrorCode)[keyof typeof ErrorCode];

export interface ValidationErrorDetail {
  field: string;
  code: ErrorCodeValue;
}
