/** Shared licensing contract — mirrors the emaapi domain model. */

export type Bilingual = { en?: string; am?: string };

/**
 * The application status vocabulary. Single source of truth for both apps —
 * previously this lived in a backoffice mock page.
 */
export type LicenseStatus =
  | "DRAFT"
  | "SUBMITTED"
  | "UNDER_REVIEW"
  | "UNDER_EVALUATION"
  | "RESUBMIT_REQUIRED"
  | "INSPECTION_PENDING"
  | "INSPECTION_COMPLETED"
  | "APPROVED"
  | "REJECTED"
  | "ON_HOLD"
  | "PAYMENT_PENDING"
  | "PAID"
  | "PAYMENT_CONFIRMED"
  | "CERTIFICATE_ISSUED"
  | "COMPLETED";

export type ApplicationKind = "NEW" | "RENEWAL";

export type FormFieldType =
  | "TEXT"
  | "TEXTAREA"
  | "NUMBER"
  | "MONEY"
  | "DATE"
  | "SELECT"
  | "BOOLEAN"
  | "EMAIL"
  | "PHONE"
  | "TIN";

export interface FieldCondition {
  field: string;
  equals?: string | number | boolean;
  notEquals?: string | number | boolean;
  in?: (string | number)[];
  isSet?: boolean;
}

export interface FormFieldConfig {
  key: string;
  label: Bilingual;
  type: FormFieldType;
  required?: boolean;
  helpText?: Bilingual;
  options?: { value: string; label: Bilingual }[];
  min?: number;
  max?: number;
  showWhen?: FieldCondition;
  readOnly?: boolean;
  source?: string;
  sortOrder?: number;
}

export interface FormSectionConfig {
  key: string;
  title: Bilingual;
  description?: Bilingual;
  fields: FormFieldConfig[];
  showWhen?: FieldCondition;
  sortOrder?: number;
  /** Sections sharing a group render together on one wizard step. */
  group?: string;
  /** Position of the group in the stepper; lowest value in a group wins. */
  groupOrder?: number;
}

/** Grouping the portal organises the licence catalogue by. */
export type LicenseCategory =
  | 'CARGO_FREIGHT'
  | 'SHIPPING_AGENCY'
  | 'INVESTMENT'
  | 'MARITIME_PERSONNEL';

export interface LicenseCategoryDefinition {
  key: LicenseCategory;
  name: Bilingual;
  description: Bilingual;
  sortOrder: number;
}

/**
 * A mode of operation the applicant has declared — the licence types they may
 * file new applications for. `key` and `name` are denormalised from the
 * licence type so the profile screen can render the set without also loading
 * the whole catalogue.
 */
export interface OperatorType {
  licenseTypeId: string;
  key: string | null;
  name: Bilingual | null;
  declaredAt: string | null;
}

export interface LicenseType {
  id: string;
  key: string;
  name: Bilingual;
  description?: Bilingual;
  category: LicenseCategory;
  certificatePrefix: string;
  feeNewApplication: string | number | null;
  feeRenewal: string | number | null;
  feeCurrency: string;
  capitalThreshold: string | number | null;
  validityMonths: number;
  /**
   * Target turnaround in hours. Null means this type is not tracked against
   * an SLA, which the grid renders as "—" rather than as instantly overdue.
   */
  slaHours: number | null;
  inspectionRequired: boolean;
  issuesCertificate: boolean;
  renewalEnabled: boolean;
  /**
   * False for person-centric registrations (seafarer): they are open to any
   * authenticated applicant, live outside the operator catalogue, and have
   * their own entry points.
   */
  requiresOperatorMode: boolean;
  formSchema: { sections: FormSectionConfig[] };
  isActive: boolean;
  /** Display order set by EMA; lower comes first. */
  sortOrder: number;
}

export interface DocumentRequirement {
  id: string;
  key: string;
  name: Bilingual;
  description?: Bilingual;
  applicationKind: ApplicationKind;
  mode: "ALWAYS" | "CONDITIONAL" | "OPTIONAL";
  conditionExpression?: FieldCondition & { previousDocExpired?: string };
  allowedMimeTypes: string[];
  maxSizeMb: number;
  requiresValidityDates: boolean;
  sortOrder: number;
}

export interface StaffEvidenceRequirement {
  docKey: string;
  label: Bilingual;
  mandatory: boolean;
}

export interface StaffRoleRequirement {
  id: string;
  roleKey: string;
  name: Bilingual;
  minCount: number;
  maxCount: number | null;
  requiresExperience: boolean;
  minYearsExperience: number | null;
  requiredEvidence: StaffEvidenceRequirement[];
  sortOrder: number;
}

export interface LicenseTypeRequirements {
  licenseType: LicenseType;
  applicationKind: ApplicationKind;
  fee: number | null;
  feeCurrency: string;
  documentRequirements: DocumentRequirement[];
  staffRoleRequirements: StaffRoleRequirement[];
}

export interface LicenseApplication {
  id: string;
  applicationNumber: string;
  licenseTypeId: string;
  licenseType?: LicenseType;
  applicantUserId: string;
  kind: ApplicationKind;
  status: LicenseStatus;
  assignedOfficerId: string | null;
  claimedAt: string | null;
  formData: Record<string, Record<string, unknown>>;
  companyName: string | null;
  tradeName: string | null;
  tinNumber: string | null;
  businessAddress: string | null;
  capitalAmountDeclared: string | null;
  capitalAmountVerified: string | null;
  adjustmentRound: number;
  submittedAt: string | null;
  decidedAt: string | null;
  rejectionReason: string | null;
  feeAmount: string | null;
  feeCurrency: string;
  issuedLicenseId: string | null;
  createdAt: string;
}

export interface ApplicationStaff {
  id: string;
  roleKey: string;
  fullName: string;
  position: string | null;
  roleCategory: string | null;
  yearsOfExperience: number | null;
  documents?: { id: string; documentKey: string; files: AttachmentFile[] }[];
}

export interface AttachmentFile {
  id: string;
  originalName: string;
  mimeType: string;
  sizeBytes: number;
  storageKey: string;
  url?: string;
}

export interface Attachment {
  id: string;
  ownerType: string;
  ownerId: string;
  documentKey: string;
  title: string | null;
  validFrom: string | null;
  validTo: string | null;
  files: AttachmentFile[];
  /** From BaseEntity. Used to place the upload on the activity trail. */
  createdAt?: string;
}

export interface StatusHistoryEntry {
  id: string;
  fromStatus: LicenseStatus | null;
  toStatus: LicenseStatus;
  event: string;
  actorUserId: string | null;
  actorName: string | null;
  actorRole: string | null;
  remark: string | null;
  metadata: Record<string, unknown> | null;
  createdAt: string;
}

export type RemarkTargetType = "FORM_SECTION" | "DOCUMENT" | "STAFF";

export interface ApplicationRemark {
  id: string;
  roundNumber: number;
  targetType: RemarkTargetType;
  targetKey: string;
  remark: string;
  isResolved: boolean;
  resolvedAt: string | null;
  createdAt: string;
}

export interface ApplicationDetail {
  application: LicenseApplication;
  staff: ApplicationStaff[];
  attachments: Attachment[];
  history: StatusHistoryEntry[];
  remarks: ApplicationRemark[];
  openRemarks: ApplicationRemark[];
  availableEvents: string[];
}

export interface Inspection {
  id: string;
  applicationId: string;
  inspectorId: string | null;
  inspectorName: string | null;
  scheduledDate: string | null;
  conductedDate: string | null;
  location: string | null;
  status: "SCHEDULED" | "COMPLETED" | "CANCELLED";
  result: "PASSED" | "FAILED" | null;
  findings: string | null;
}

export interface AppNotification {
  id: string;
  subject: Bilingual;
  content: Bilingual;
  isSeen: boolean;
  itemId: string | null;
  itemType: string | null;
  metadata: Record<string, unknown> | null;
  createdAt: string;
}

/** Server-side queue filters. Mirrors ApplicationQueueFilterDto in the API. */
export interface QueueFilter {
  licenseTypeId?: string;
  search?: string;
  status?: LicenseStatus[];
  /** Officer uuid, or the literal 'unassigned'. */
  assignee?: string;
  submittedFrom?: string;
  submittedTo?: string;
  overdue?: boolean;
  sortBy?: QueueSortField;
  sortDir?: "ASC" | "DESC";
  take?: number;
  skip?: number;
}

export type QueueSortField =
  | "submittedAt"
  | "applicationNumber"
  | "companyName"
  | "status"
  | "dueAt"
  | "claimedAt";

/** Row counts behind the queue's saved-view tabs. */
export interface QueueCounts {
  unassigned: number;
  mine: number;
  awaitingApplicant: number;
  overdue: number;
  readyToIssue: number;
  all: number;
}

export type DocumentDecision = "ACCEPTED" | "REJECTED";

/** An officer's verdict on one uploaded document. */
export interface DocumentReview {
  id: string;
  applicationId: string;
  documentKey: string;
  attachmentId: string | null;
  decision: DocumentDecision;
  reason: string | null;
  reviewedById: string | null;
  reviewedByName: string | null;
  updatedAt: string;
}

/** An officer-defined queue filter, persisted server-side. */
export interface SavedQueueView {
  id: string;
  ownerUserId: string;
  name: string;
  queryString: string;
  isShared: boolean;
  isDefault: boolean;
  sortOrder: number;
}

export interface AssignableOfficer {
  id: string;
  name: string | null;
}

/** Full filtered result set for export. `truncated` when the cap was hit. */
export interface ExportResult {
  items: LicenseApplication[];
  total: number;
  truncated: boolean;
}

export type TemplateStatus = "DRAFT" | "PUBLISHED" | "ARCHIVED";

export interface TemplatePageOptions {
  format?: "A4" | "A5" | "Letter" | "Legal";
  landscape?: boolean;
  printBackground?: boolean;
}

/** A certificate design authored in the backoffice. */
export interface LicenseTemplate {
  id: string;
  licenseTypeId: string;
  name: string;
  version: number;
  hbsSource: string;
  pageOptions: TemplatePageOptions | null;
  status: TemplateStatus;
  publishedAt: string | null;
  createdAt: string;
  updatedAt: string;
}

/** A placeholder a certificate design may use. */
export interface TemplateVariable {
  key: string;
  label: string;
}

export interface Paginated<T> {
  total: number;
  items: T[];
}

/** Per-field problems returned by the server when a submission is incomplete. */
export interface ValidationIssue {
  kind: "field" | "document" | "staff";
  target: string;
  field?: string;
  message: string;
}

/** What the browser must do to complete a payment. */
export interface ClientAction {
  type: "REDIRECT" | "LAUNCH_APP" | "NONE";
  url?: string;
  appId?: string;
  receiveCode?: string;
  shortCode?: string;
}

export type PaymentStatus =
  "PENDING" | "PROCESSING" | "PAID" | "FAILED" | "EXPIRED" | "CANCELLED";

export interface InitiatePaymentResult {
  paymentId: string;
  intentId: string;
  status: PaymentStatus;
  amount: number;
  currency: string;
  provider: string;
  clientAction?: ClientAction;
}

export interface ApplicationPayment {
  id: string;
  applicationId: string;
  paymentIntentId: string;
  amount: string;
  currency: string;
  provider: string;
  providerRef: string | null;
  merchantOrderId: string | null;
  status: PaymentStatus | null;
  checkoutUrl: string | null;
  paidAt: string | null;
  failureReason: string | null;
}

/** A licence certificate issued to the applicant. */
export interface IssuedLicense {
  /** Why the licence was suspended, revoked or cancelled. */
  statusReason?: string | null;
  statusChangedAt?: string | null;
  statusChangedByName?: string | null;
  id: string;
  certificateNumber: string;
  licenseTypeId: string;
  licenseType?: LicenseType;
  applicationId: string;
  companyName: string | null;
  tinNumber: string | null;
  issueDate: string;
  expiryDate: string;
  status: "ACTIVE" | "EXPIRED" | "SUSPENDED" | "CANCELLED" | "SUPERSEDED";
  /**
   * Days until the expiry date; negative once it has passed. Computed by the
   * API in the authority's timezone — the client must not re-derive it, since
   * a browser in another zone would land on a different day.
   */
  daysUntilExpiry?: number;
  /**
   * Whether a renewal can be filed now: inside the licence type's renewal
   * window, or after expiry. The API decides, because the window is per-type
   * configuration.
   */
  renewable?: boolean;
  verificationCode: string;
  certificateFileKey: string | null;
}
