import { z } from "zod";

export const CreateCampaignSchema = z.object({
  name: z.string().trim().min(1, "Name is required").max(255),
});

export const UpdateCampaignSchema = z.object({
  name: z.string().trim().min(1, "Name is required").max(255),
});

export const ListCampaignsQuerySchema = z.object({
  search: z.string().optional(),
  page: z.string().regex(/^\d+$/).optional(),
  pageSize: z.string().regex(/^\d+$/).optional(),
});

export const AttachPromoCodesSchema = z.object({
  promo_codes: z
    .array(
      z.object({
        promo_code_id: z.string().min(1),
        promo_code_name: z.string().min(1),
      }),
    )
    .min(1, "At least one promo code is required"),
});

// Bulk detach — one request removes many mappings (one DELETE, one log).
export const BulkDetachPromoCodesSchema = z.object({
  promo_code_ids: z
    .array(z.string().min(1))
    .min(1, "At least one promo code id is required"),
});

export const AnalyticsQuerySchema = z.object({
  from: z.string().optional(),
  to: z.string().optional(),
  /*
   * Repeated query parameters, e.g. ?baseStatus=ACTIVE&baseStatus=PENDING.
   *
   * Typed as a bare string because that is what a single occurrence parses to; the
   * handler reads the full list through `c.req.queries()`, since the flat `query()`
   * form keeps only the last occurrence.
   */
  baseStatus: z.string().optional(),
  accountStatus: z.string().optional(),
});

export const UsersByPromoCodeQuerySchema = z.object({
  page: z.string().regex(/^\d+$/).optional(),
  pageSize: z.string().regex(/^\d+$/).optional(),
  search: z.string().optional(),
  // "true" restricts the list to members who have logged in at least once.
  loggedIn: z.enum(["true", "false"]).optional(),
  // Repeated parameters — see AnalyticsQuerySchema.
  baseStatus: z.string().optional(),
  accountStatus: z.string().optional(),
});

export const SignupsSummarySchema = z.object({
  codes: z.array(z.string().min(1)).min(1, "At least one code is required"),
  from: z.string().optional(),
  to: z.string().optional(),
  /**
   * Account-status scope, shared by every promo endpoint.
   *
   * `baseStatus` is ACTIVE | PENDING | EXPIRED | STOPPED and is expanded server-side
   * against the console DB's `member_account_status_base`; `accountStatus` names
   * individual statuses. The two are unioned, so "base ACTIVE plus Cancelled" means
   * nine statuses rather than none.
   */
  baseStatus: z.array(z.string().min(1)).optional(),
  accountStatus: z.array(z.string().min(1)).optional(),
});
export type SignupsSummaryRequest = z.infer<typeof SignupsSummarySchema>;

// Flexible Reports section — combined paginated user list across one or more
// promo codes. POST (not GET) because the code list can be long.
export const ReportUsersSchema = z.object({
  // Empty / omitted ⇒ all promo-code signups (the default "all members" view).
  codes: z.array(z.string().min(1)).optional().default([]),
  page: z.number().int().positive().optional(),
  pageSize: z.number().int().positive().max(1000).optional(),
  search: z.string().optional(),
  loggedIn: z.boolean().optional(),
  // Signup window (matched against members.created_at) + country, server-side.
  from: z.string().optional(),
  to: z.string().optional(),
  country: z.array(z.string().min(1)).optional(),
  // Refine filters — applied server-side so pagination.total reflects them.
  status: z.enum(["active", "inactive"]).optional(),
  nationality: z.array(z.string().min(1)).optional(),
  accountType: z.array(z.string().min(1)).optional(),
  /**
   * Account statuses by name, e.g. ["Active"], plus the base-status shorthand.
   *
   * Separate from `status` above, which is only a two-way split on whether the name is
   * exactly "Active" — eight of the 25 statuses start with that word, so the boolean
   * cannot name them. `baseStatus` is expanded server-side and unioned with these.
   */
  accountStatus: z.array(z.string().min(1)).optional(),
  baseStatus: z.array(z.string().min(1)).optional(),
  // Date-of-birth range (YYYY-MM-DD, inclusive).
  dobFrom: z.string().optional(),
  dobTo: z.string().optional(),
  /**
   * Contact-completeness bucket, matching the aggregate the chart is built from.
   *
   * Lets the Contact Completeness page list exactly the members behind a slice.
   */
  contact: z.enum(["both", "phone", "email", "none"]).optional(),
  /** Login status, for the Logged In drill-down. */
  loginStatus: z.enum(["logged_in", "pending"]).optional(),
  /** Only members with no country recorded — the "No Country Info" area. */
  countryMissing: z.boolean().optional(),
});
export type ReportUsersRequest = z.infer<typeof ReportUsersSchema>;

export const ReportFilterOptionsSchema = z.object({
  codes: z.array(z.string().min(1)).optional().default([]),
  from: z.string().optional(),
  to: z.string().optional(),
});
export type ReportFilterOptionsRequest = z.infer<
  typeof ReportFilterOptionsSchema
>;

// Per-code, per-country signup aggregate for a window. Powers the Promo Codes
// report's window-aware Registered/Logged-In counts and country options.
export const SignupAggregateSchema = z.object({
  codes: z.array(z.string().min(1)).min(1, "At least one code is required"),
  from: z.string().optional(),
  to: z.string().optional(),
  country: z.array(z.string().min(1)).optional(),
  /**
   * Account-status scope, shared by every promo endpoint.
   *
   * `baseStatus` is ACTIVE | PENDING | EXPIRED | STOPPED and is expanded server-side
   * against the console DB's `member_account_status_base`; `accountStatus` names
   * individual statuses. The two are unioned, so "base ACTIVE plus Cancelled" means
   * nine statuses rather than none.
   */
  baseStatus: z.array(z.string().min(1)).optional(),
  accountStatus: z.array(z.string().min(1)).optional(),
});
export type SignupAggregateRequest = z.infer<typeof SignupAggregateSchema>;

// Per-campaign signup totals for a window (+ optional country). Aggregates
// across each campaign's promo codes in one pass.
export const CampaignSignupsSchema = z.object({
  from: z.string().optional(),
  to: z.string().optional(),
  country: z.array(z.string().min(1)).optional(),
  /**
   * Account-status scope, shared by every promo endpoint.
   *
   * `baseStatus` is ACTIVE | PENDING | EXPIRED | STOPPED and is expanded server-side
   * against the console DB's `member_account_status_base`; `accountStatus` names
   * individual statuses. The two are unioned, so "base ACTIVE plus Cancelled" means
   * nine statuses rather than none.
   */
  baseStatus: z.array(z.string().min(1)).optional(),
  accountStatus: z.array(z.string().min(1)).optional(),
});
export type CampaignSignupsRequest = z.infer<typeof CampaignSignupsSchema>;

export type CreateCampaignRequest = z.infer<typeof CreateCampaignSchema>;
export type UpdateCampaignRequest = z.infer<typeof UpdateCampaignSchema>;
export type ListCampaignsQuery = z.infer<typeof ListCampaignsQuerySchema>;
export type AttachPromoCodesRequest = z.infer<typeof AttachPromoCodesSchema>;
export type BulkDetachPromoCodesRequest = z.infer<
  typeof BulkDetachPromoCodesSchema
>;
export type AnalyticsQuery = z.infer<typeof AnalyticsQuerySchema>;
export type UsersByPromoCodeQuery = z.infer<typeof UsersByPromoCodeQuerySchema>;
