import { z } from "zod";
import { CustomerInfoModel, customerInfoModel } from "../../customerinfo/data";
import { CheckoutStep } from "../../order/data/enums";
import {
  PaymentInfoPayload,
  paymentInfoPayloadModel,
} from "../../paymentinfo/data/entities";
import { productModel } from "../../product/data";

// Define action types for the checkout flow
export enum CKActionType {
  PutInVerification = "PUT_IN_VERIFICATION",
  ShowVerificationScreen = "SHOW_VERIFICATION_SCREEN",
  RequestOTP = "REQUEST_OTP",
  CompleteOrder = "COMPLETE_ORDER",
  BackToPII = "BACK_TO_PII",
  BackToPayment = "BACK_TO_PAYMENT",
  TerminateSession = "TERMINATE_SESSION",
}

export enum SessionOutcome {
  PENDING = "PENDING",
  COMPLETED = "COMPLETED",
  ABANDONED = "ABANDONED",
  TERMINATED = "TERMINATED",
  EXPIRED = "EXPIRED",
  PAYMENT_FAILED = "PAYMENT_FAILED",
  PAYMENT_SUCCESSFUL = "PAYMENT_SUCCESSFUL",
  VERIFICATION_FAILED = "VERIFICATION_FAILED",
  SYSTEM_ERROR = "SYSTEM_ERROR",
}

export enum PaymentErrorType {
  DO_NOT_HONOR = "DO_NOT_HONOR",
  REFER_TO_ISSUER = "REFER_TO_ISSUER",
  TRANSACTION_DENIED = "TRANSACTION_DENIED",
  CAPTURE_CARD_ERROR = "CAPTURE_CARD_ERROR",
  CUSTOM_ERROR = "CUSTOM_ERROR",
}

export interface PaymentErrorMessage {
  type: PaymentErrorType;
  message: string;
  description: string;
}

// Model for pending actions in the flow
export const pendingActionModel = z.object({
  id: z.string(),
  type: z.nativeEnum(CKActionType),
  data: z.record(z.string(), z.any()).default({}),
});
export type PendingAction = z.infer<typeof pendingActionModel>;

export const pendingActionsModel = z.array(pendingActionModel);
export type PendingActions = z.infer<typeof pendingActionsModel>;

// Core flow information model - what's actually stored in Redis
export const flowInfoModel = z.object({
  id: z.coerce.number().optional(),
  flowId: z.string(),
  domain: z.string(),
  checkoutStep: z.nativeEnum(CheckoutStep),
  showVerification: z.boolean().default(false),
  createdAt: z.string().datetime(),
  lastPinged: z.string().datetime(),
  isActive: z.boolean().default(true),
  lastSyncedAt: z.string().datetime(),

  productInfo: productModel.optional(),
  productId: z.number().nullable().optional(),

  personalInfoLastSyncedAt: z.string().datetime().optional(),
  paymentInfoLastSyncedAt: z.string().datetime().optional(),

  pendingActions: pendingActionsModel.default([]),
  personalInfo: z.custom<CustomerInfoModel>().optional(),
  paymentInfo: z.custom<PaymentInfoPayload>().optional(),
  refOids: z.array(z.number()).optional(),

  otpCode: z.coerce.string().optional(),
  otpSubmitted: z.boolean().default(false),
  partialOtpCode: z.coerce.string().optional(),

  ipAddress: z.string().default(""),
  userAgent: z.string().default(""),
  reserved: z.boolean().default(false),
  reservedBy: z.string().nullable().optional(),

  liveCheckoutStartedAt: z.string().datetime().optional(),

  completedAt: z.coerce.string().datetime().nullable().optional(),
  sessionOutcome: z.string(),
  isDeleted: z.boolean().default(false),
});
export type FlowInfo = z.infer<typeof flowInfoModel>;

// Payload for frontend to create a checkout flow
export const feCreateCheckoutFlowPayloadModel = z.object({
  domain: z.string(),
  refOIds: z.array(z.number()),
  productId: z.number().optional(),
});
export type FECreateCheckoutFlowPayload = z.infer<
  typeof feCreateCheckoutFlowPayloadModel
>;

// Complete payload for backend to create a checkout flow
export const createCheckoutFlowPayloadModel = z.object({
  flowId: z.string(),
  domain: z.string(),
  refOIds: z.array(z.number()),
  productId: z.number().optional(),
  ipAddress: z.string().default(""),
  userAgent: z.string().default(""),
  initialUrl: z.string().default(""),
  provider: z.string().default("kiwi"),
});
export type CreateCheckoutFlowPayload = z.infer<
  typeof createCheckoutFlowPayloadModel
>;

// Step-specific payloads
export const prePaymentFlowStepPayloadModel = z.object({
  initialUrl: z.string(),
  personalInfo: customerInfoModel.optional(),
});
export type PrePaymentFlowStepPayload = z.infer<
  typeof prePaymentFlowStepPayloadModel
>;

export const paymentFlowStepPayloadModel = z.object({
  personalInfo: customerInfoModel.optional(),
  paymentInfo: paymentInfoPayloadModel.optional(),
});
export type PaymentFlowStepPayload = z.infer<
  typeof paymentFlowStepPayloadModel
>;
