// TypeScript interfaces mirroring API response shapes

import type { ReactNode } from 'react';

export interface PageHeaderConfig {
  title?: string;
  subtitle?: string;
  actions?: ReactNode;
  fullBleed?: boolean;
}

export interface AppOutletContext {
  setPageHeader: (config: PageHeaderConfig | null) => void;
}

export type UserRole = 'SUPER_ADMIN' | 'INFLUENCE_ADMIN' | 'MAP_ADMIN' | 'BROADCAST_ADMIN' | 'CONTENT_ADMIN' | 'MEDIA_ADMIN' | 'PAYMENTS_ADMIN' | 'EVENTS_ADMIN' | 'SOCIAL_ADMIN' | 'POLLS_ADMIN' | 'ANALYTICS_ADMIN' | 'USER' | 'TEMP';

export type UserStatus = 'ACTIVE' | 'INACTIVE' | 'SUSPENDED' | 'EXPIRED' | 'PENDING_VERIFICATION' | 'PENDING_APPROVAL';

export type CreatedVia = 'ADMIN' | 'PUBLIC_SHIFT_SIGNUP' | 'STANDARD' | 'SELF_REGISTRATION' | 'QUICK_JOIN_INVITE';

export interface User {
  id: string;
  email: string;
  name: string | null;
  phone: string | null;
  pronouns: string | null;
  role: UserRole;
  roles: UserRole[];
  status: UserStatus;
  permissions: Record<string, unknown> | null;
  createdVia: CreatedVia;
  expiresAt: string | null;
  expireDays: number | null;
  lastLoginAt: string | null;
  emailVerified: boolean;
  createdAt: string;
  updatedAt: string;
}

export interface AuthResponse {
  user: User;
  accessToken?: string;
  refreshToken?: string;
  requiresVerification?: boolean;
  message?: string;
}

export interface PaginationMeta {
  page: number;
  limit: number;
  total: number;
  totalPages: number;
}

export interface UsersListResponse {
  users: User[];
  pagination: PaginationMeta;
}

export interface ApiError {
  error: {
    message: string;
    code: string;
    details?: Record<string, string[]>;
  };
}

export interface CreateUserPayload {
  email: string;
  password: string;
  name?: string;
  phone?: string;
  pronouns?: string;
  role?: UserRole;
  roles?: UserRole[];
  status?: UserStatus;
  expiresAt?: string;
  expireDays?: number;
}

export interface UpdateUserPayload {
  email?: string;
  password?: string;
  name?: string;
  phone?: string;
  pronouns?: string;
  role?: UserRole;
  roles?: UserRole[];
  status?: UserStatus;
  expiresAt?: string | null;
  expireDays?: number;
  permissions?: Record<string, unknown>;
}

export interface UsersListParams {
  page?: number;
  limit?: number;
  search?: string;
  role?: UserRole;
  status?: UserStatus;
}

export const ADMIN_ROLES: UserRole[] = ['SUPER_ADMIN', 'INFLUENCE_ADMIN', 'MAP_ADMIN', 'BROADCAST_ADMIN', 'CONTENT_ADMIN', 'MEDIA_ADMIN', 'PAYMENTS_ADMIN', 'EVENTS_ADMIN', 'SOCIAL_ADMIN', 'POLLS_ADMIN', 'ANALYTICS_ADMIN'];

// Module-specific role groups
export const INFLUENCE_ROLES: UserRole[] = ['SUPER_ADMIN', 'INFLUENCE_ADMIN'];
export const MAP_ROLES: UserRole[] = ['SUPER_ADMIN', 'MAP_ADMIN'];
export const BROADCAST_ROLES: UserRole[] = ['SUPER_ADMIN', 'BROADCAST_ADMIN'];
export const CONTENT_ROLES: UserRole[] = ['SUPER_ADMIN', 'CONTENT_ADMIN'];
export const MEDIA_ROLES: UserRole[] = ['SUPER_ADMIN', 'MEDIA_ADMIN'];
export const PAYMENTS_ROLES: UserRole[] = ['SUPER_ADMIN', 'PAYMENTS_ADMIN'];
export const EVENTS_ROLES: UserRole[] = ['SUPER_ADMIN', 'EVENTS_ADMIN'];
export const SOCIAL_ROLES: UserRole[] = ['SUPER_ADMIN', 'SOCIAL_ADMIN'];
export const SYSTEM_ROLES: UserRole[] = ['SUPER_ADMIN'];
export const SCHEDULING_ROLES: UserRole[] = ['SUPER_ADMIN', 'MAP_ADMIN', 'EVENTS_ADMIN'];
export const POLLS_ROLES: UserRole[] = ['SUPER_ADMIN', 'POLLS_ADMIN', 'INFLUENCE_ADMIN'];
export const ANALYTICS_ROLES: UserRole[] = ['SUPER_ADMIN', 'ANALYTICS_ADMIN'];

// --- User Provisioning ---

export type ProvisionStatus = 'synced' | 'pending' | 'error' | 'not_provisioned' | 'invited';

export interface ServiceProvisioningStatus {
  serviceKey: string;
  displayName: string;
  status: ProvisionStatus;
  serviceUserId?: string;
  lastError?: string;
  details?: Record<string, unknown>;
}

export interface ProvisioningStatusResponse {
  services: ServiceProvisioningStatus[];
}

export interface ProvisionAllResult {
  results: { serviceKey: string; displayName: string; success: boolean; error?: string }[];
}

export interface BulkSyncResult {
  total: number;
  provisioned: number;
  errors: number;
  details: Record<string, { success: number; failed: number }>;
}

// --- Campaigns ---

export type CampaignStatus = 'DRAFT' | 'ACTIVE' | 'PAUSED' | 'ARCHIVED';

export type CampaignModerationStatus = 'PENDING_REVIEW' | 'APPROVED' | 'REJECTED' | 'CHANGES_REQUESTED';

export type GovernmentLevel = 'FEDERAL' | 'PROVINCIAL' | 'MUNICIPAL' | 'SCHOOL_BOARD';

export interface Campaign {
  id: string;
  slug: string;
  title: string;
  description: string | null;
  emailSubject: string;
  emailBody: string;
  callToAction: string | null;
  coverPhoto: string | null;
  coverVideoId: number | null;
  status: CampaignStatus;
  allowSmtpEmail: boolean;
  allowMailtoLink: boolean;
  collectUserInfo: boolean;
  showEmailCount: boolean;
  showCallCount: boolean;
  allowEmailEditing: boolean;
  allowCustomRecipients: boolean;
  showResponseWall: boolean;
  highlightCampaign: boolean;
  targetGovernmentLevels: GovernmentLevel[];
  createdByUserId: string | null;
  createdByUserEmail: string | null;
  createdByUserName: string | null;
  isUserGenerated: boolean;
  moderationStatus: CampaignModerationStatus | null;
  reviewedByUserId: string | null;
  reviewedAt: string | null;
  rejectionReason: string | null;
  moderationNotes: string | null;
  createdAt: string;
  updatedAt: string;
  _count: {
    emails: number;
    responses: number;
  };
}

export interface CampaignsListResponse {
  campaigns: Campaign[];
  pagination: PaginationMeta;
}

export interface CreateCampaignPayload {
  title: string;
  emailSubject: string;
  emailBody: string;
  description?: string;
  callToAction?: string;
  status?: CampaignStatus;
  targetGovernmentLevels?: GovernmentLevel[];
  allowSmtpEmail?: boolean;
  allowMailtoLink?: boolean;
  collectUserInfo?: boolean;
  showEmailCount?: boolean;
  showCallCount?: boolean;
  allowEmailEditing?: boolean;
  allowCustomRecipients?: boolean;
  showResponseWall?: boolean;
  highlightCampaign?: boolean;
  coverPhoto?: string;
  coverVideoId?: number | null;
}

export interface UpdateCampaignPayload {
  title?: string;
  emailSubject?: string;
  emailBody?: string;
  description?: string | null;
  callToAction?: string | null;
  status?: CampaignStatus;
  targetGovernmentLevels?: GovernmentLevel[];
  allowSmtpEmail?: boolean;
  allowMailtoLink?: boolean;
  collectUserInfo?: boolean;
  showEmailCount?: boolean;
  showCallCount?: boolean;
  allowEmailEditing?: boolean;
  allowCustomRecipients?: boolean;
  showResponseWall?: boolean;
  highlightCampaign?: boolean;
  coverPhoto?: string | null;
  coverVideoId?: number | null;
}

export interface CampaignsListParams {
  page?: number;
  limit?: number;
  search?: string;
  status?: CampaignStatus;
}

// --- User-Generated Campaigns ---

export interface CreateUserCampaignPayload {
  title: string;
  description?: string;
  emailSubject: string;
  emailBody: string;
  callToAction?: string;
  targetGovernmentLevels: GovernmentLevel[];
}

export interface ModerateCampaignPayload {
  action: 'approve' | 'reject' | 'request_changes';
  reason?: string;
  notes?: string;
}

export interface CampaignModerationStats {
  total: number;
  pending: number;
  approved: number;
  rejected: number;
  changesRequested: number;
}

export const CAMPAIGN_MODERATION_STATUS_COLORS: Record<CampaignModerationStatus, string> = {
  PENDING_REVIEW: 'orange',
  APPROVED: 'green',
  REJECTED: 'red',
  CHANGES_REQUESTED: 'gold',
};

export const CAMPAIGN_MODERATION_STATUS_LABELS: Record<CampaignModerationStatus, string> = {
  PENDING_REVIEW: 'Pending Review',
  APPROVED: 'Approved',
  REJECTED: 'Rejected',
  CHANGES_REQUESTED: 'Changes Requested',
};

// --- Representatives ---

export interface RepresentativeOffice {
  type?: string;
  tel?: string;
  fax?: string;
  postal?: string;
}

export interface Representative {
  id: string | null;
  postalCode: string;
  name: string | null;
  email: string | null;
  districtName: string | null;
  electedOffice: string | null;
  partyName: string | null;
  representativeSetName: string | null;
  url: string | null;
  photoUrl: string | null;
  offices: RepresentativeOffice[] | null;
  cachedAt: string;
}

export interface RepresentativesListResponse {
  representatives: Representative[];
  pagination: PaginationMeta;
}

export interface RepresentativeLookupResponse {
  source: 'cache' | 'api';
  postalCode: string;
  location: {
    city: string | null;
    province: string | null;
  };
  representatives: Representative[];
}

export interface CacheStats {
  totalRepresentatives: number;
  postalCodesWithRepresentatives: number;
  totalPostalCodes: number;
}

export interface RepresentativesListParams {
  page?: number;
  limit?: number;
  search?: string;
  postalCode?: string;
}

// --- Campaign Emails ---

export type EmailMethod = 'SMTP' | 'MAILTO';

export type CampaignEmailStatus = 'QUEUED' | 'SENT' | 'FAILED' | 'CLICKED' | 'USER_INFO_CAPTURED';

export interface CampaignEmail {
  id: string;
  userEmail: string | null;
  userName: string | null;
  userPostalCode: string | null;
  recipientEmail: string;
  recipientName: string | null;
  recipientLevel: GovernmentLevel | null;
  emailMethod: EmailMethod;
  subject: string;
  status: CampaignEmailStatus;
  sentAt: string;
}

export interface CampaignEmailsListResponse {
  emails: CampaignEmail[];
  pagination: PaginationMeta;
}

export interface CampaignEmailStats {
  total: number;
  queued: number;
  sent: number;
  failed: number;
  clicked: number;
  smtpCount: number;
  mailtoCount: number;
}

export interface QueueStats {
  waiting: number;
  active: number;
  completed: number;
  failed: number;
  paused: boolean;
}

// --- Representative Responses ---

export type ResponseType = 'EMAIL' | 'LETTER' | 'PHONE_CALL' | 'MEETING' | 'SOCIAL_MEDIA' | 'OTHER';

export type ResponseStatus = 'PENDING' | 'APPROVED' | 'REJECTED';

export interface RepresentativeResponse {
  id: string;
  representativeName: string;
  representativeTitle: string | null;
  representativeLevel: GovernmentLevel;
  representativeEmail: string | null;
  responseType: ResponseType;
  responseText: string;
  userComment?: string | null;       // Admin-only on public response wall (2026-04-12)
  submittedByName?: string | null;   // Admin-only on public response wall (2026-04-12)
  submittedByEmail?: string | null;

  isAnonymous: boolean;
  status: ResponseStatus;
  isVerified: boolean;
  verifiedAt: string | null;
  verifiedBy: string | null;
  upvoteCount: number;
  createdAt: string;
  campaign?: {
    id: string;
    title: string;
    slug: string;
  };
}

export interface ResponsesListResponse {
  responses: RepresentativeResponse[];
  pagination: PaginationMeta;
}

export interface ResponseStats {
  total: number;
  verified: number;
  totalUpvotes: number;
  byLevel: Record<string, number>;
}

export interface SubmitResponsePayload {
  representativeName: string;
  representativeLevel: GovernmentLevel;
  responseType: ResponseType;
  responseText: string;
  representativeTitle?: string;
  representativeEmail?: string;
  userComment?: string;
  submittedByName?: string;
  submittedByEmail?: string;
  isAnonymous?: boolean;
  sendVerification?: boolean;
}

export interface ResponsesListParams {
  page?: number;
  limit?: number;
  status?: ResponseStatus;
  campaignId?: string;
  search?: string;
}

export interface PublicResponsesListParams {
  page?: number;
  limit?: number;
  sort?: 'recent' | 'upvotes' | 'verified';
  level?: GovernmentLevel;
}

export const RESPONSE_TYPE_LABELS: Record<ResponseType, string> = {
  EMAIL: 'Email',
  LETTER: 'Letter',
  PHONE_CALL: 'Phone Call',
  MEETING: 'Meeting',
  SOCIAL_MEDIA: 'Social Media',
  OTHER: 'Other',
};

export const RESPONSE_STATUS_COLORS: Record<ResponseStatus, string> = {
  PENDING: 'orange',
  APPROVED: 'green',
  REJECTED: 'red',
};

export const GOVERNMENT_LEVEL_COLORS: Record<GovernmentLevel, string> = {
  FEDERAL: 'blue',
  PROVINCIAL: 'purple',
  MUNICIPAL: 'green',
  SCHOOL_BOARD: 'orange',
};

export const GOVERNMENT_LEVEL_LABELS: Record<GovernmentLevel, string> = {
  FEDERAL: 'Federal',
  PROVINCIAL: 'Provincial',
  MUNICIPAL: 'Municipal',
  SCHOOL_BOARD: 'School Board',
};

// --- Map / Locations ---

export type SupportLevel = 'LEVEL_1' | 'LEVEL_2' | 'LEVEL_3' | 'LEVEL_4';

export type GeocodeProvider = 'GOOGLE' | 'MAPBOX' | 'NOMINATIM' | 'PHOTON' | 'LOCATIONIQ' | 'ARCGIS' | 'UNKNOWN';

export type BuildingType = 'SINGLE_FAMILY' | 'MULTI_UNIT' | 'MIXED_USE' | 'COMMERCIAL';

export interface Location {
  id: string;
  latitude: string; // Prisma Decimal comes as string (required)
  longitude: string; // Prisma Decimal comes as string (required)

  // Building-level data
  address: string; // Base street address (no unit number)
  postalCode: string | null;
  province: string | null;
  federalDistrict: string | null;
  buildingUse: number | null;

  // NAR + building metadata
  locGuid: string | null;
  buildingType: BuildingType;
  totalUnits: number;
  buildingNotes: string | null;

  // Geocoding
  geocodeConfidence: number | null;
  geocodeProvider: GeocodeProvider | null;

  // Audit
  createdByUserId: string | null;
  updatedByUserId: string | null;
  createdAt: string;
  updatedAt: string;

  // Relations (optional, loaded via include)
  addresses?: Address[];
}

export interface Address {
  id: string;
  locationId: string;

  // Unit identification
  unitNumber: string | null;
  addrGuid: string | null;

  // Occupant/contact info (per-unit)
  firstName: string | null;
  lastName: string | null;
  email: string | null;
  phone: string | null;

  // Canvassing data (per-unit)
  supportLevel: SupportLevel | null;
  sign: boolean;
  signSize: string | null;
  notes: string | null;

  // Audit
  createdByUserId: string | null;
  updatedByUserId: string | null;
  createdAt: string;
  updatedAt: string;

  // Relations (optional, loaded via include)
  location?: Location;
}

export interface LocationsListResponse {
  locations: Location[];
  pagination: PaginationMeta;
}

export interface LocationStats {
  total: number;
  buildingTypes: {
    SINGLE_FAMILY: number;
    MULTI_UNIT: number;
    MIXED_USE: number;
    COMMERCIAL: number;
  };
  geocoded: number;
  ungeocoded: number;
  confidence: {
    high: number;
    medium: number;
    low: number;
    none: number;
    average: number | null;
  };
  providers: {
    nominatim: number;
    mapbox: number;
    arcgis: number;
    photon: number;
    google: number;
    locationiq: number;
    manual: number;
  };
}

export type LocationHistoryAction =
  | 'CREATED'
  | 'UPDATED'
  | 'GEOCODED'
  | 'BULK_GEOCODED'
  | 'MOVED_ON_MAP'
  | 'IMPORTED_CSV'
  | 'IMPORTED_NAR';

export interface LocationHistory {
  id: string;
  locationId: string;
  userId: string | null;
  user: {
    id: string;
    email: string;
    name: string | null;
    role: UserRole;
  } | null;
  action: LocationHistoryAction;
  field: string | null;
  oldValue: string | null;
  newValue: string | null;
  metadata: Record<string, unknown> | null;
  createdAt: string;
}

export interface LocationHistoryResponse {
  history: LocationHistory[];
  pagination: PaginationMeta;
}

export const LOCATION_HISTORY_ACTION_LABELS: Record<LocationHistoryAction, string> = {
  CREATED: 'Created',
  UPDATED: 'Updated',
  GEOCODED: 'Geocoded',
  BULK_GEOCODED: 'Bulk Re-Geocoded',
  MOVED_ON_MAP: 'Moved on Map',
  IMPORTED_CSV: 'Imported (CSV)',
  IMPORTED_NAR: 'Imported (NAR)',
};

export const LOCATION_HISTORY_ACTION_COLORS: Record<LocationHistoryAction, string> = {
  CREATED: 'green',
  UPDATED: 'blue',
  GEOCODED: 'orange',
  BULK_GEOCODED: 'purple',
  MOVED_ON_MAP: 'cyan',
  IMPORTED_CSV: 'geekblue',
  IMPORTED_NAR: 'magenta',
};

export interface MapSettings {
  id: string;
  latitude: string | null;
  longitude: string | null;
  zoom: number | null;
  walkSheetTitle: string | null;
  walkSheetSubtitle: string | null;
  walkSheetFooter: string | null;
  qrCode1Url: string | null;
  qrCode1Label: string | null;
  qrCode2Url: string | null;
  qrCode2Label: string | null;
  qrCode3Url: string | null;
  qrCode3Label: string | null;
  publicMapEnabled: boolean;
  publicShowLocations: boolean;
  publicShowSupportLevels: boolean;
  publicShowCuts: boolean;
  publicShowEvents: boolean;
  publicShowAddresses: boolean;
  publicShowSignInfo: boolean;
  createdAt: string;
  updatedAt: string;
}

export interface GeocodeResult {
  latitude: number;
  longitude: number;
  confidence: number;
  provider: GeocodeProvider;
  formattedAddress?: string;
}

export interface LocationsListParams {
  page?: number;
  limit?: number;
  search?: string;
  supportLevel?: SupportLevel;
  hasSign?: boolean;
  confidenceLevel?: 'high' | 'medium' | 'low' | 'none';
  sortBy?: 'createdAt' | 'address' | 'supportLevel';
  sortOrder?: 'asc' | 'desc';
}

export const SUPPORT_LEVEL_LABELS: Record<SupportLevel, string> = {
  LEVEL_1: 'Strong Support',
  LEVEL_2: 'Likely Support',
  LEVEL_3: 'Unsure',
  LEVEL_4: 'Opposition',
};

export const SUPPORT_LEVEL_COLORS: Record<SupportLevel, string> = {
  LEVEL_1: '#27ae60',
  LEVEL_2: '#f1c40f',
  LEVEL_3: '#e67e22',
  LEVEL_4: '#e74c3c',
};

export interface ReverseGeocodeResult {
  address: string;
  city?: string;
  province?: string;
  country?: string;
}

// --- Map / Cuts ---

export type CutCategory = 'CUSTOM' | 'WARD' | 'NEIGHBORHOOD' | 'DISTRICT';

export interface Cut {
  id: string;
  name: string;
  description: string | null;
  color: string;
  opacity: string; // Prisma Decimal comes as string
  category: CutCategory | null;
  isPublic: boolean;
  isOfficial: boolean;
  geojson: string;
  bounds: string | null;
  showLocations: boolean;
  exportEnabled: boolean;
  assignedTo: string | null;
  filterSettings: Record<string, unknown> | null;
  lastCanvassed: string | null;
  completionPercentage: number;
  createdByUserId: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface PublicCut {
  id: string;
  name: string;
  description: string | null;
  color: string;
  opacity: string;
  category: CutCategory | null;
  geojson: string;
  bounds: string | null;
}

export interface CutsListResponse {
  cuts: Cut[];
  pagination: PaginationMeta;
}

export interface CreateCutPayload {
  name: string;
  geojson: string;
  description?: string;
  color?: string;
  opacity?: number;
  category?: CutCategory;
  isPublic?: boolean;
  isOfficial?: boolean;
  bounds?: string;
  showLocations?: boolean;
  exportEnabled?: boolean;
  assignedTo?: string;
}

export interface UpdateCutPayload {
  name?: string;
  description?: string | null;
  color?: string;
  opacity?: number;
  category?: CutCategory | null;
  isPublic?: boolean;
  isOfficial?: boolean;
  geojson?: string;
  bounds?: string | null;
  showLocations?: boolean;
  exportEnabled?: boolean;
  assignedTo?: string | null;
  lastCanvassed?: string | null;
  completionPercentage?: number;
}

export interface CutsListParams {
  page?: number;
  limit?: number;
  category?: CutCategory;
  search?: string;
}

export interface CutStatistics {
  total: number;
  byLevel: Record<string, number>;
  withSign: number;
}

export const CUT_CATEGORY_LABELS: Record<CutCategory, string> = {
  CUSTOM: 'Custom',
  WARD: 'Ward',
  NEIGHBORHOOD: 'Neighborhood',
  DISTRICT: 'District',
};

export const CUT_CATEGORY_COLORS: Record<CutCategory, string> = {
  CUSTOM: 'default',
  WARD: 'blue',
  NEIGHBORHOOD: 'green',
  DISTRICT: 'purple',
};

export const MAP_ADMIN_ROLES: UserRole[] = MAP_ROLES;

// --- Map / Shifts ---

export type ShiftStatus = 'OPEN' | 'FULL' | 'CANCELLED';

export type SignupStatus = 'CONFIRMED' | 'CANCELLED';

export type SignupSource = 'AUTHENTICATED' | 'PUBLIC' | 'ADMIN';

export type RecurrenceFrequency = 'DAILY' | 'WEEKLY' | 'MONTHLY';

export type ShiftKind = 'CANVASS' | 'TRAINING' | 'EVENT_STAFFING' | 'PHONE_BANK' | 'OTHER';

export const SHIFT_KIND_LABELS: Record<ShiftKind, string> = {
  CANVASS: 'Canvass',
  TRAINING: 'Training',
  EVENT_STAFFING: 'Event Staffing',
  PHONE_BANK: 'Phone Bank',
  OTHER: 'Other',
};

export const SHIFT_KIND_COLORS: Record<ShiftKind, string> = {
  CANVASS: 'blue',
  TRAINING: 'green',
  EVENT_STAFFING: 'purple',
  PHONE_BANK: 'orange',
  OTHER: 'default',
};

export interface Shift {
  id: string;
  title: string;
  description: string | null;
  date: string;
  startTime: string;
  endTime: string;
  location: string | null;
  maxVolunteers: number;
  currentVolunteers: number;
  status: ShiftStatus;
  kind: ShiftKind;
  isPublic: boolean;
  cutId: string | null;
  cut?: { id: string; name: string } | null;
  ticketedEventId?: string | null;
  ticketedEvent?: { id: string; slug: string; title: string; date: string } | null;
  meetingId?: string | null;
  meeting?: { id: string; slug: string; title: string; isActive: boolean; jitsiRoom?: string } | null;
  seriesId?: string | null;
  series?: ShiftSeries;
  isException?: boolean;
  createdBy: string | null;
  createdAt: string;
  updatedAt: string;
  _count?: {
    signups: number;
  };
  signups?: ShiftSignup[];
}

export interface ShiftSignup {
  id: string;
  shiftId: string;
  shiftTitle: string | null;
  userId: string | null;
  userEmail: string;
  userName: string | null;
  userPhone: string | null;
  signupDate: string;
  status: SignupStatus;
  signupSource: SignupSource;
  user?: {
    id: string;
    email: string;
    name: string | null;
    phone: string | null;
  };
}

export interface ShiftSeries {
  id: string;
  title: string;
  description: string | null;
  startTime: string;
  endTime: string;
  location: string | null;
  maxVolunteers: number;
  isPublic: boolean;
  cutId: string | null;
  cut?: { id: string; name: string } | null;
  frequency: RecurrenceFrequency;
  daysOfWeek: number[] | null;
  startDate: string;
  endDate: string | null;
  createdBy: string | null;
  createdAt: string;
  updatedAt: string;
  shifts?: Shift[];
}

export interface CreateShiftSeriesInput {
  title: string;
  description?: string;
  startTime: string;
  endTime: string;
  location?: string;
  maxVolunteers: number;
  isPublic?: boolean;
  cutId?: string;
  kind?: ShiftKind;
  frequency: RecurrenceFrequency;
  daysOfWeek?: number[];
  startDate: string;
  endDate?: string;
}

export interface CalendarData {
  dates: Record<string, {
    count: number;
    shifts: Shift[];
  }>;
}

export interface EditMode {
  mode: 'THIS' | 'FUTURE' | 'ALL';
  fromDate?: string;
}

export interface ShiftsListResponse {
  shifts: Shift[];
  pagination: PaginationMeta;
}

export interface ShiftStats {
  total: number;
  open: number;
  full: number;
  cancelled: number;
  upcoming: number;
  totalSignups: number;
}

export interface ShiftsListParams {
  page?: number;
  limit?: number;
  search?: string;
  status?: ShiftStatus;
  kind?: ShiftKind;
  upcoming?: boolean;
  sortBy?: 'date' | 'createdAt' | 'title';
  sortOrder?: 'asc' | 'desc';
}

export interface CreateShiftPayload {
  title: string;
  description?: string;
  date: string;
  startTime: string;
  endTime: string;
  location?: string;
  maxVolunteers: number;
  isPublic?: boolean;
  cutId?: string;
}

export interface UpdateShiftPayload {
  title?: string;
  description?: string | null;
  date?: string;
  startTime?: string;
  endTime?: string;
  location?: string | null;
  maxVolunteers?: number;
  isPublic?: boolean;
  status?: ShiftStatus;
  cutId?: string | null;
}

export const SHIFT_STATUS_COLORS: Record<ShiftStatus, string> = {
  OPEN: 'green',
  FULL: 'orange',
  CANCELLED: 'red',
};

export const SHIFT_STATUS_LABELS: Record<ShiftStatus, string> = {
  OPEN: 'Open',
  FULL: 'Full',
  CANCELLED: 'Cancelled',
};

export const SIGNUP_SOURCE_COLORS: Record<SignupSource, string> = {
  AUTHENTICATED: 'blue',
  PUBLIC: 'green',
  ADMIN: 'purple',
};

// --- Landing Pages ---

export type EditorMode = 'VISUAL' | 'CODE';

export type MkdocsExportMode = 'THEMED' | 'STANDALONE';

export interface LandingPage {
  id: string;
  slug: string;
  title: string;
  description: string | null;
  editorMode: EditorMode;
  blocks: unknown; // GrapesJS project data
  htmlOutput: string | null;
  cssOutput: string | null;
  mkdocsPath: string | null;
  mkdocsStubPath: string | null;
  mkdocsExportMode: MkdocsExportMode;
  mkdocsHideNav: boolean;
  mkdocsHideToc: boolean;
  mkdocsSkipExport: boolean;
  published: boolean;
  listed: boolean;
  seoTitle: string | null;
  seoDescription: string | null;
  seoImage: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface PageBlock {
  id: string;
  type: string;
  label: string;
  schema: Record<string, unknown>;
  defaults: Record<string, unknown>;
  thumbnail: string | null;
  category: string | null;
  sortOrder: number;
  createdAt: string;
  updatedAt: string;
}

export interface LandingPagesListResponse {
  pages: LandingPage[];
  pagination: PaginationMeta;
}

export interface LandingPagesListParams {
  page?: number;
  limit?: number;
  search?: string;
  published?: 'true' | 'false';
}

// --- Docs / Code Server ---

export interface DocsStatus {
  mkdocs: { online: boolean; url: string };
  codeServer: { online: boolean; url: string };
  siteServer: { online: boolean; url: string };
}

export interface DocsConfig {
  codeServerPort: number;
  mkdocsPort: number;
  mkdocsSitePort: number;
}

// --- Docs / File Tree ---

export interface FileNode {
  name: string;
  path: string;
  isDirectory: boolean;
  children?: FileNode[];
}

// --- Platform Services ---

export interface ServicesStatus {
  nocodb: { online: boolean; url: string };
  n8n: { online: boolean; url: string };
  gitea: { online: boolean; url: string };
  mailhog: { online: boolean; url: string };
  miniqr: { online: boolean; url: string };
  excalidraw: { online: boolean; url: string };
  homepage: { online: boolean; url: string };
  vaultwarden: { online: boolean; url: string };
  rocketchat: { online: boolean; url: string };
  gancio: { online: boolean; url: string };
  jitsi: { online: boolean; url: string };
}

export interface ServicesConfig {
  nocodbPort: number;
  n8nPort: number;
  giteaPort: number;
  mailhogPort: number;
  miniqrPort: number;
  excalidrawPort: number;
  domain: string;
  nocodbSubdomain: string;
  n8nSubdomain: string;
  giteaSubdomain: string;
  mailhogSubdomain: string;
  miniqrSubdomain: string;
  excalidrawSubdomain: string;
  // Listmonk (newsletter platform)
  listmonkPort: number;
  listmonkSubdomain: string;
  // Code Server (web IDE)
  codeServerPort: number;
  codeServerSubdomain: string;
  // MkDocs (documentation)
  mkdocsPort: number;
  mkdocsSubdomain: string;
  // Grafana (metrics visualization)
  grafanaPort: number;
  grafanaSubdomain: string;
  // Alertmanager (alert routing)
  alertmanagerPort: number;
  alertmanagerSubdomain: string;
  // Homepage (service dashboard)
  homepagePort: number;
  homepageSubdomain: string;
  // Vaultwarden (password manager)
  vaultwardenPort: number;
  vaultwardenSubdomain: string;
  // Rocket.Chat (team chat)
  rocketchatPort: number;
  rocketchatSubdomain: string;
  // Gancio (event management)
  gancioPort: number;
  gancioSubdomain: string;
  // Jitsi Meet (video conferencing)
  jitsiPort: number;
  jitsiSubdomain: string;
}

// --- Site Settings ---

export interface SiteSettings {
  id: string;
  organizationName: string;
  organizationShortName: string;
  organizationLogoUrl: string | null;
  organizationFaviconUrl: string | null;
  adminColorPrimary: string;
  adminColorBgBase: string;
  publicColorPrimary: string;
  publicColorBgBase: string;
  publicColorBgContainer: string;
  publicHeaderGradient: string;
  footerText: string;
  loginSubtitle: string;
  emailFromName: string;
  // SMTP fields (only present from admin endpoint)
  smtpHost?: string;
  smtpPort?: number;
  smtpUser?: string;
  smtpPass?: string;
  smtpFromAddress?: string;
  smtpActiveProvider?: 'mailhog' | 'production';
  emailTestMode?: boolean;
  testEmailRecipient?: string;
  _effective?: {
    provider: 'mailhog' | 'production';
    host: string;
    port: number;
    user: string;
    hasPassword: boolean;
    fromAddress: string;
    fromName: string;
    testMode: boolean;
    testRecipient: string;
  };
  enablePublicRegistration: boolean;
  enableEmailVerification: boolean;
  autoApproveVerifiedUsers: boolean;
  enableInfluence: boolean;
  enableMap: boolean;
  enableNewsletter: boolean;
  enableLandingPages: boolean;
  enableMediaFeatures: boolean;
  enablePayments: boolean;
  enableGalleryAds: boolean;
  enableChat: boolean;
  enableEvents: boolean;
  enableSms: boolean;
  enablePeople: boolean;
  enableSocial: boolean;
  enableMeet: boolean;
  enableMeetingPlanner: boolean;
  enableTicketedEvents: boolean;
  enableSocialCalendar: boolean;
  enablePetitions: boolean;
  enablePolls: boolean;
  enableAnalytics: boolean;
  analyticsRetentionDays: number;
  analyticsGeoEnabled: boolean;
  trackAuthenticatedUsers: boolean;
  enableDocsCollaboration: boolean;
  requireEventApproval: boolean;
  autoSyncPeopleToMap: boolean;
  // SMS connection config (only present from admin endpoint)
  smsTermuxApiUrl?: string;
  smsTermuxApiKey?: string;
  smsTailscaleApiKey?: string;
  smsTailscaleTailnet?: string;
  smsTailscaleDeviceId?: string;
  smsTailscaleDeviceName?: string;
  // Auto-upgrade settings (only present from admin endpoint)
  enableAutoUpgrade?: boolean;
  autoUpgradeSchedule?: 'daily-3am' | 'daily-4am' | 'daily-5am' | 'weekly-sun-3am' | 'weekly-mon-3am' | '12h' | '24h';
  autoUpgradePullServices?: boolean;
  notifyAdminAutoUpgrade?: boolean;
  // Registry settings
  useRegistryForUpgrade?: boolean;
  giteaRegistryUrl?: string;
  // Navigation configuration
  navConfig: NavConfig | null;
  // User Provisioning
  enableUserProvisioning: boolean;
  provisionGitea: boolean;
  provisionGiteaTiming: 'lazy' | 'eager';
  provisionVaultwarden: boolean;
  provisionVaultwardenTiming: 'lazy' | 'eager';
  provisionListmonk: boolean;
  provisionListmonkTiming: 'lazy' | 'eager';
  // SMS notification settings
  smsShiftReminders: boolean;
  smsShiftReminderHours: number;
  smsShiftSignupConfirm: boolean;
  smsVolunteerWelcome: boolean;
  // Notification settings
  notifyAdminShiftSignup: boolean;
  notifyAdminResponseSubmitted: boolean;
  notifyAdminSignRequested: boolean;
  notifyAdminShiftCancellation: boolean;
  notifyVolunteerSessionSummary: boolean;
  notifyVolunteerCancellation: boolean;
  notifyVolunteerShiftReminder: boolean;
  createdAt: string;
  updatedAt: string;
}

// --- Meetings (Jitsi) ---

export interface Meeting {
  id: string;
  slug: string;
  title: string;
  description: string | null;
  jitsiRoom: string;
  isActive: boolean;
  createdByUserId: string;
  createdAt: string;
  updatedAt: string;
  startTime: string | null;
  endTime: string | null;
}

// --- Navigation Config (shared nav bar) ---

export interface NavItem {
  id: string;
  label: string;
  path: string;           // Empty string '' for group items
  icon: string;
  enabled: boolean;
  order: number;
  type: 'builtin' | 'custom' | 'group';
  featureFlag?: string;
  external?: boolean;
  children?: NavItem[];   // One level deep only (for groups)
}

export interface NavConfig {
  items: NavItem[];
}

// --- MkDocs Header Builder ---

export interface HeaderNavItem {
  id: string;
  label: string;
  path: string;
  icon?: string;
  enabled: boolean;
  order: number;
  type: 'builtin' | 'custom';
  openInNewTab?: boolean;
}

export interface HeaderStyle {
  backgroundColor: string;
  textColor: string;
  hoverColor: string;
  height: string;
}

export interface HeaderConfig {
  enabled: boolean;
  items: HeaderNavItem[];
  style: HeaderStyle;
}

// --- MkDocs Config ---

export interface MkDocsConfigResponse {
  content: string;
}

export interface MkDocsBuildResult {
  success: boolean;
  output: string;
  duration: number;
}

// --- Pangolin ---

export interface PangolinStatus {
  configured: boolean;
  healthy: boolean;
  pangolinUrl: string | null;
  orgId: string | null;
  siteId: string | null;
  newtConfigured: boolean;
  siteIdValid: boolean | null;
  resolvedSiteId: string | null;
  siteIdMismatch: boolean;
}

export interface PangolinConfig {
  pangolinApiUrl: string | null;
  pangolinEndpoint: string | null;
  orgId: string | null;
  siteId: string | null;
  newtId: string | null;
  newtConfigured: boolean;
  domain: string;
}

export interface PangolinSite {
  siteId: string;
  name: string;
  orgId: string;
  niceId: string;
  pubKey?: string;
  subnet?: string;
  address?: string;
  megabytesIn?: number;
  megabytesOut?: number;
  lastSeen?: string;
  online?: boolean;
  type?: string;
  isCurrentSite?: boolean;
}

export interface ConnectSiteResult {
  success: boolean;
  site: {
    siteId: string;
    name: string;
    niceId: string;
    online: boolean;
  };
  envUpdate: {
    success: boolean;
    updated: string[];
    added: string[];
    error?: string;
  };
  message: string;
}

export interface PangolinExitNode {
  exitNodeId: string;
  name: string;
  location?: string;
  region?: string;
  online: boolean;
  capacity?: number;
  latency?: number;
}

export interface PangolinResource {
  resourceId: string;
  siteId: string;
  orgId: string;
  name: string;
  subdomain?: string;
  fullDomain?: string;
  ssl?: boolean;
  blockAccess?: boolean;
  active?: boolean;
  proxyPort?: number;
  protocol?: string;
  domainBindings?: string[];
}

export interface PangolinNewtStatus {
  newtConfigured: boolean;
  containerRunning: boolean;
  containerStatus: string;
  ready: boolean;
  error?: string;
}

export interface NewtRestartResult {
  success: boolean;
  message: string;
  output: string;
}

export interface PangolinCertificate {
  certId: string;
  domainId: string;
  domain: string;
  status: 'PENDING' | 'ACTIVE' | 'EXPIRED' | 'FAILED';
  issuedAt?: string;
  expiresAt?: string;
  autoRenew?: boolean;
  issuer?: string;
}

export interface PangolinConnectedClient {
  clientId: string;
  resourceId: string;
  ipAddress: string;
  connectedAt: string;
  lastSeen: string;
  bytesIn: number;
  bytesOut: number;
  online: boolean;
}

// --- Pangolin Resource Status ---

export interface ResourceStatusItem {
  name: string;
  subdomain: string | null;
  fullDomain: string;
  required: boolean;
  container: string | null;
  profile: string | null;
  expectedTargetIp: string | null;
  expectedTargetPort: number | null;
  exists: boolean;
  resourceId: string | number | null;
  hasTarget: boolean;
  targetCorrect: boolean;
  actualTargetIp: string | null;
  actualTargetPort: number | null;
  targetId: string | null;
  targetEnabled: boolean | null;
  ssl: boolean | null;
  sso: boolean | null;
  blockAccess: boolean | null;
  enabled: boolean | null;
}

export interface ResourceStatusResponse {
  resources: ResourceStatusItem[];
  extras: ResourceStatusItem[];
  summary: {
    total: number;
    healthy: number;
    misconfigured: number;
    missing: number;
    extras: number;
  };
  siteId: string;
  domain: string;
}

export interface SyncResult {
  success: boolean;
  created: number;
  targetFixed: number;
  skipped: number;
  warnings: number;
  errors: number;
  details: {
    created: string[];
    targetFixed: string[];
    skipped: string[];
    warnings: string[];
    errors: string[];
  };
}

// --- Listmonk ---

export interface ListmonkStatus {
  enabled: boolean;
  connected: boolean;
  initialized: boolean;
  lastSyncAt: string | null;
  lastError: string | null;
}

export interface ListmonkListStat {
  name: string;
  subscriberCount: number;
}

export interface ListmonkStats {
  lists: ListmonkListStat[];
}

export interface ListmonkSyncResult {
  success: boolean;
  message: string;
  results?: {
    total: number;
    success: number;
    failed: number;
    errors: string[];
  };
}

// --- Geocoding ---

export interface GeocodeSearchResult {
  latitude: number;
  longitude: number;
  displayName: string;
  type: string;
  provider: string;
}

// --- Bulk Import ---

export interface BulkImportResult {
  total: number;
  created: number;
  skippedDuplicate: number;
  skippedOutOfBounds: number;
  skippedInvalid: number;
  errors: string[];
}

// --- NAR Server Import ---

export interface NarDataset {
  provinceCode: string;
  provinceName: string;
  provinceAbbr: string;
  addressFiles: { filename: string; sizeFormatted: string; sizeBytes: number }[];
  locationFiles: { filename: string; sizeFormatted: string; sizeBytes: number }[];
  totalAddressSize: number;
  totalLocationSize: number;
}

export interface NarDatasetsResponse {
  narDir: string | null;
  datasets: NarDataset[];
}

export interface NarServerImportResult {
  provinceCode: string;
  provinceName: string;
  totalRows: number;
  created: number;
  skippedDuplicate: number;
  skippedOutOfBounds: number;
  skippedNonResidential: number;
  skippedInvalid: number;
  errors: string[];
  durationMs: number;
}

export interface NarImportProgress {
  status: 'loading-locations' | 'importing' | 'complete' | 'failed';
  totalRows: number;
  locationsCreated: number;
  addressesCreated: number;
  skippedDuplicate: number;
  skippedOutOfBounds: number;
  skippedNonResidential: number;
  skippedInvalid: number;
  currentFile: string;
  provinceName: string;
  error?: string;
  result?: NarServerImportResult;
}

// --- Area Import ---

export type AreaImportSourceStatus = 'pending' | 'running' | 'complete' | 'failed' | 'skipped';

export interface AreaImportSourceProgress {
  status: AreaImportSourceStatus;
  candidatesFound: number;
  failedQuadrants?: number;
  message?: string;
  error?: string;
}

export interface AreaImportProgress {
  status: 'initializing' | 'running' | 'creating-records' | 'complete' | 'failed';
  bounds: { minLat: number; maxLat: number; minLng: number; maxLng: number } | null;
  areaSqKm: number;
  sources: {
    osm: AreaImportSourceProgress;
    nar: AreaImportSourceProgress;
    reverseGeocode: AreaImportSourceProgress;
  };
  locationsCreated: number;
  addressesCreated: number;
  skippedDuplicate: number;
  totalCandidates: number;
  error?: string;
}

export interface AreaImportPreviewResult {
  bounds: { minLat: number; maxLat: number; minLng: number; maxLng: number };
  areaSqKm: number;
  existingLocations: number;
  estimates: {
    osm: number;
    nar: number;
    reverseGeocode: number;
  };
  narProvincesDetected: string[];
}

// --- SMTP Test Results ---

export interface SmtpTestResult {
  success: boolean;
  message: string;
}

export interface SmtpSendTestResult {
  success: boolean;
  messageId?: string;
  testMode: boolean;
  recipient: string;
}

export interface ListmonkSyncAllResult {
  success: boolean;
  message: string;
  results?: {
    participants: { total: number; success: number; failed: number; errors: string[] };
    locations: { total: number; success: number; failed: number; errors: string[] };
    users: { total: number; success: number; failed: number; errors: string[] };
  };
}

// --- Observability / Monitoring ---

export interface ServiceStatus {
  online: boolean;
  url: string;
}

export interface ObservabilityStatus {
  prometheus: ServiceStatus;
  grafana: ServiceStatus;
  alertmanager: ServiceStatus;
  cadvisor: ServiceStatus;
  nodeExporter: ServiceStatus;
  redisExporter: ServiceStatus;
  gotify: ServiceStatus;
}

export interface MetricValue {
  value: number;
  unit: string;
}

export interface MetricsSummary {
  apiUptime: MetricValue;
  emailQueueSize: MetricValue;
  requestRate: MetricValue;
  emailErrorRate: MetricValue;
  activeSessions: MetricValue;
  activeCanvassSessions: MetricValue;
  redisHealth: MetricValue;
}

export interface AlertInfo {
  id: string;
  severity: 'critical' | 'warning' | 'info';
  name: string;
  summary: string;
  description?: string;
  startsAt: string;
}

export interface AlertsResponse {
  total: number;
  critical: number;
  warning: number;
  info: number;
  alerts: AlertInfo[];
}

// --- Email Templates ---

export type EmailTemplateCategory = 'INFLUENCE' | 'MAP' | 'SYSTEM';

export interface EmailTemplate {
  id: string;
  key: string;
  name: string;
  description: string | null;
  category: EmailTemplateCategory;
  subjectLine: string;
  htmlContent: string;
  textContent: string;
  isSystem: boolean;
  isActive: boolean;
  variables: EmailTemplateVariable[];
  _count?: { versions: number; testLogs: number };
  createdBy: { id: string; name: string; email: string };
  updatedBy?: { id: string; name: string; email: string } | null;
  createdAt: string;
  updatedAt: string;
}

export interface EmailTemplateVariable {
  id: string;
  key: string;
  label: string;
  description: string | null;
  type: 'TEXT' | 'VIDEO';
  videoId: number | null;
  isRequired: boolean;
  isConditional: boolean;
  sampleValue: string | null;
  sortOrder: number;
}

export interface EmailTemplateVersion {
  id: string;
  versionNumber: number;
  subjectLine: string;
  htmlContent: string;
  textContent: string;
  changeNotes: string | null;
  createdBy: { id: string; name: string; email: string };
  createdAt: string;
}

export interface EmailTemplateTestLog {
  id: string;
  recipientEmail: string;
  testData: Record<string, string>;
  success: boolean;
  errorMessage: string | null;
  sentBy: { id: string; name: string; email: string };
  sentAt: string;
}

export interface EmailTemplatesListResponse {
  templates: EmailTemplate[];
  pagination: PaginationMeta;
}

export interface EmailTemplatesListParams {
  page?: number;
  limit?: number;
  search?: string;
  category?: EmailTemplateCategory;
  isActive?: boolean;
}

export interface CreateEmailTemplateDto {
  key: string;
  name: string;
  description?: string;
  category: EmailTemplateCategory;
  subjectLine: string;
  htmlContent: string;
  textContent: string;
  isActive?: boolean;
  variables?: CreateEmailTemplateVariableDto[];
}

export interface UpdateEmailTemplateDto {
  name?: string;
  description?: string;
  category?: EmailTemplateCategory;
  subjectLine?: string;
  htmlContent?: string;
  textContent?: string;
  isActive?: boolean;
  variables?: CreateEmailTemplateVariableDto[];
}

export interface CreateEmailTemplateVariableDto {
  key: string;
  label: string;
  description?: string;
  type?: 'TEXT' | 'VIDEO';
  videoId?: number;
  isRequired?: boolean;
  isConditional?: boolean;
  sampleValue?: string;
  sortOrder?: number;
}

export interface ValidationResult {
  valid: boolean;
  errors: string[];
  warnings?: string[];
  extractedVariables?: string[];
}

export interface SendTestEmailDto {
  recipientEmail: string;
  testData: Record<string, string>;
}

export const EMAIL_TEMPLATE_CATEGORY_LABELS: Record<EmailTemplateCategory, string> = {
  INFLUENCE: 'Influence',
  MAP: 'Map',
  SYSTEM: 'System',
};

export const EMAIL_TEMPLATE_CATEGORY_COLORS: Record<EmailTemplateCategory, string> = {
  INFLUENCE: 'blue',
  MAP: 'green',
  SYSTEM: 'purple',
};

// --- Dashboard ---

export interface DashboardSummary {
  users: {
    total: number;
    byRole: Record<string, number>;
    active: number;
    suspended: number;
  };
  campaigns: {
    total: number;
    active: number;
    draft: number;
    paused: number;
    archived: number;
  };
  locations: {
    total: number;
    geocoded: number;
    addresses: number;
  };
  emails: {
    total: number;
    sent: number;
    failed: number;
    queued: number;
  };
  shifts: {
    total: number;
    open: number;
    upcoming: number;
  };
  canvass: {
    totalSessions: number;
    totalVisits: number;
    activeSessions: number;
  };
  responses: {
    total: number;
    pending: number;
    approved: number;
  };
  videos: {
    total: number;
    published: number;
  };
  pages: {
    total: number;
    published: number;
  };
  emailTemplates: {
    total: number;
  };
  cuts: {
    total: number;
  };
  representatives: {
    totalCached: number;
  };
  campaignModeration: {
    pendingReview: number;
  };
  docsComments: {
    pending: number;
  };
}

export interface SystemInfo {
  hostname: string;
  platform: string;
  arch: string;
  nodeVersion: string;
  uptime: number;
  cpu: {
    model: string;
    cores: number;
    loadAvg: number[];
  };
  memory: {
    totalMB: number;
    usedMB: number;
    freeMB: number;
    usagePercent: number;
  };
  disk: {
    totalGB: number;
    usedGB: number;
    freeGB: number;
    usagePercent: number;
  } | null;
  process: {
    heapUsedMB: number;
    heapTotalMB: number;
    rssMB: number;
    uptimeSeconds: number;
  };
}

export interface ContainerInfo {
  name: string;
  label: string;
  running: boolean;
  status: string;
}

export interface WeatherData {
  latitude: number;
  longitude: number;
  temperature: number;
  apparentTemperature: number;
  humidity: number;
  windSpeed: number;
  windDirection: number;
  weatherCode: number;
  weatherDescription: string;
  isDay: boolean;
  precipitation: number;
  cloudCover: number;
  time: string;
}

export interface ApiMetrics {
  requestRate: number;
  errorRate: number;
  avgLatencyMs: number;
  p95LatencyMs: number;
  topRoutes: { method: string; route: string; count: number }[];
  slowRoutes: { method: string; route: string; p95Ms: number }[];
  statusBreakdown: { status: string; count: number }[];
}

// --- Canvass Export ---

export interface ExportContactsPreviewResult {
  totalContacts: number;
  contactsWithEmail: number;
  byCut: { cutId: string; cutName: string; contacts: number; withEmail: number }[];
  byOutcome: Record<string, number>;
  bySupportLevel: Record<string, number>;
}

export interface ExportContactsResult {
  created: number;
  skippedDuplicate: number;
  skippedNoEmail: number;
  campaignId: string;
  campaignTitle: string;
}

export interface CutCampaignAnalytics {
  cutId: string;
  cutName: string;
  totalAddresses: number;
  visitedAddresses: number;
  completionPct: number;
  addressesWithEmail: number;
  supportBreakdown: Record<string, number>;
}

// --- Dashboard Activity Feed ---

export interface ActivityItem {
  id: string;
  type: 'shift_signup' | 'response_submitted' | 'canvass_completed' | 'email_sent' | 'user_created';
  module: 'map' | 'influence' | 'users';
  title: string;
  description: string;
  timestamp: string;
}

export interface ActivityFeedResult {
  items: ActivityItem[];
  total: number;
  page: number;
  limit: number;
}

// --- Dashboard Connectivity ---

export interface ConnectivityStatus {
  smtp: boolean;
  listmonk: boolean;
  rocketchat: boolean;
  gancio: boolean;
}

// --- Dashboard Today Events (Gancio) ---

export interface TodayEvent {
  id: number;
  title: string;
  description: string;
  placeName: string;
  startTime: string;
  endTime: string | null;
  tags: string[];
}

export interface TodayEventsResult {
  enabled: boolean;
  events: TodayEvent[];
  total: number;
}

// --- Dashboard Chat Summary (Rocket.Chat) ---

export interface ChatMessage {
  id: string;
  channel: string;
  username: string;
  text: string;
  timestamp: string;
  isBot: boolean;
}

export interface ChatSummaryResult {
  enabled: boolean;
  messages: ChatMessage[];
  unreadChannels: number;
}

// --- Dashboard Time-Series ---

export interface TimeSeriesPoint {
  timestamps: number[];
  values: number[];
}

export type TimeSeriesResult = Record<string, TimeSeriesPoint>;

export type TimeSeriesMetricKey =
  | 'request_rate_2xx' | 'request_rate_4xx' | 'request_rate_5xx'
  | 'latency_p50' | 'latency_p95' | 'latency_p99'
  | 'email_sent_rate' | 'email_failed_rate' | 'email_queue_size'
  | 'cpu_usage' | 'memory_usage' | 'active_sessions' | 'login_rate';

// --- Dashboard Container Resources ---

export interface ContainerResource {
  name: string;
  label: string;
  cpuPercent: number;
  memoryMB: number;
  memoryLimitMB: number;
  networkRxKBps: number;
  networkTxKBps: number;
}

export interface ContainerResourcesResponse {
  containers: ContainerResource[];
}

// --- Payments ---

export interface SubscriptionPlan {
  id: number;
  name: string;
  priceCAD: number;
  durationDays: number;
  yearlyPriceCAD: number | null;
  features: string[] | null;
  description: string | null;
  tier: number;
  displayOrder: number;
  isActive: boolean;
  stripeProductId: string | null;
  stripePriceId: string | null;
  stripeYearlyPriceId: string | null;
  slug: string | null;
  coverPhoto: string | null;
  coverVideoId: number | null;
  richDescription: string | null;
  ctaText: string | null;
  ctaSubtext: string | null;
  highlightPlan: boolean;
  createdAt: string;
  _count?: { subscriptions: number };
  activeSubscribers?: number;
}

export interface UserSubscription {
  id: number;
  userId: string;
  planId: number;
  status: string;
  startDate: string;
  endDate: string;
  cancelledAt: string | null;
  stripeSubscriptionId: string | null;
  stripeCustomerId: string | null;
  currentPeriodEnd: string | null;
  cancelAtPeriodEnd: boolean;
  createdAt: string;
  plan?: SubscriptionPlan;
  user?: { id: string; email: string; name: string | null };
}

export type ProductType = 'DIGITAL' | 'EVENT' | 'DONATION';
export type OrderStatus = 'PENDING' | 'COMPLETED' | 'FAILED' | 'REFUNDED';

export interface Product {
  id: string;
  slug: string;
  title: string;
  description: string | null;
  priceCAD: number;
  type: ProductType;
  stripeProductId: string | null;
  stripePriceId: string | null;
  isActive: boolean;
  imageUrl: string | null;
  photoId: number | null;
  videoId: number | null;
  galleryPhotoIds: number[] | null;
  downloadUrl: string | null;
  metadata: Record<string, unknown> | null;
  maxPurchases: number | null;
  purchaseCount: number;
  createdAt: string;
  updatedAt: string;
  // Computed by API (resolveMediaUrls)
  resolvedImageUrl?: string | null;
  thumbnailUrl?: string | null;
  videoThumbnailUrl?: string | null;
  videoStreamUrl?: string | null;
  galleryImages?: Array<{ photoId: number; thumbnailUrl: string; imageUrl: string }> | null;
}

export interface Order {
  id: string;
  userId: string | null;
  productId: string | null;
  amountCAD: number;
  status: OrderStatus;
  stripeCheckoutSessionId: string | null;
  stripePaymentIntentId: string | null;
  type: string;
  buyerEmail: string;
  buyerName: string | null;
  donorMessage: string | null;
  isAnonymous: boolean;
  completedAt: string | null;
  createdAt: string;
  updatedAt: string;
  product?: { id: string; title: string; slug: string; type: ProductType } | null;
  user?: { id: string; email: string; name: string | null } | null;
  donationPageId?: string | null;
  donationPage?: { id: string; title: string; slug: string } | null;
}

export type DonationPageStatus = 'DRAFT' | 'ACTIVE' | 'PAUSED' | 'ARCHIVED';

export interface DonationPage {
  id: string;
  slug: string;
  title: string;
  description: string | null;
  status: DonationPageStatus;
  suggestedAmounts: number[];
  minimumAmount: number;
  thankYouMessage: string;
  coverPhoto: string | null;
  coverVideoId: number | null;
  highlightPage: boolean;
  showDonorCount: boolean;
  showTotalRaised: boolean;
  goalAmount: number | null;
  createdAt: string;
  updatedAt: string;
  _count?: { orders: number };
  totalRaised?: number;
  donorCount?: number;
}

export interface PaymentSettings {
  id: string;
  stripeSecretKey: string;
  stripePublishableKey: string;
  stripeWebhookSecret: string;
  defaultCurrency: string;
  enableDonations: boolean;
  donationSuggestedAmounts: number[];
  donationMinimum: number;
  donationPageTitle: string;
  donationPageDescription: string | null;
  thankYouMessage: string;
  createdAt: string;
  updatedAt: string;
}

export interface PaymentDashboardStats {
  activeSubscribers: number;
  totalRevenue: number;
  mrr: number;
  planCount: number;
  donations: {
    totalDonations: number;
    totalAmount: number;
    recentDonations: Order[];
  };
}

// --- Campaign Effectiveness ---

export interface CampaignOverviewStats {
  summary: {
    totalEmails: number;
    totalResponses: number;
    totalCalls: number;
    activeCampaigns: number;
    totalCampaigns: number;
    avgResponseRate: number;
  };
  campaigns: CampaignEffectivenessRow[];
}

export interface CampaignEffectivenessRow {
  campaignId: string;
  title: string;
  slug: string;
  status: CampaignStatus;
  createdAt: string;
  emailTotal: number;
  emailBreakdown: Record<string, number>;
  responseTotal: number;
  approvedResponses: number;
  responseBreakdown: Record<string, number>;
  responseRate: number;
  callCount: number;
}

export interface RepEffectivenessData {
  representatives: RepEffectivenessRow[];
  totalRepresentatives: number;
  levelDistribution: Array<{ level: string; count: number }>;
}

export interface RepEffectivenessRow {
  name: string;
  email: string;
  level: string | null;
  emailsReceived: number;
  responsesGiven: number;
  verifiedCount: number;
  responseRate: number;
}

export interface GeoBreakdownData {
  groupBy: 'province' | 'city' | 'postalCode';
  data: GeoBreakdownRow[];
}

export interface GeoBreakdownRow {
  key: string;
  emailCount: number;
  city: string | null;
  province: string | null;
}

export interface FunnelStage {
  name: string;
  count: number;
  percentOfFirst: number;
  dropoff: number;
}

export interface ActivityTrendsData {
  granularity: 'day' | 'week';
  dateFrom: string;
  dateTo: string;
  series: Array<{ date: string; emails: number; responses: number }>;
}

// --- Dashboard Top Videos ---

export interface DashboardTopVideo {
  id: number;
  title: string | null;
  filename: string;
  viewCount: number;
  commentCount: number;
  upvoteCount: number;
  durationSeconds: number | null;
  isPublished: boolean;
}

export interface DashboardTopVideosResult {
  enabled: boolean;
  videos: DashboardTopVideo[];
}

// --- Dashboard Recent Comments ---

export interface DashboardRecentComment {
  id: number;
  content: string;
  videoId: number;
  videoTitle: string | null;
  videoFilename: string;
  authorName: string | null;
  safetyStatus: string | null;
  createdAt: string;
}

export interface DashboardRecentCommentsResult {
  enabled: boolean;
  comments: DashboardRecentComment[];
  pendingCount: number;
}

// --- Dashboard Docs Analytics ---

export interface DashboardDocsAnalytics {
  totalViews: number;
  uniqueSessions: number;
  topPages: Array<{ path: string; views: number }>;
  viewsByDay: Array<{ date: string; views: number }>;
  topReferrers?: Array<{ referrer: string; views: number }>;
}

// --- Dashboard Upcoming Shifts ---

export interface DashboardUpcomingShift {
  id: string;
  title: string;
  date: string;
  startTime: string;
  endTime: string;
  location: string | null;
  maxVolunteers: number;
  currentVolunteers: number;
  status: string;
  cutName: string | null;
}

export interface DashboardUpcomingShiftsResult {
  shifts: DashboardUpcomingShift[];
  total: number;
}

// --- Dashboard Recent Signups ---

export interface DashboardRecentSignup {
  id: string;
  userName: string | null;
  userEmail: string;
  shiftTitle: string | null;
  shiftDate: string | null;
  signupDate: string;
  signupSource: string;
}

export interface DashboardRecentSignupsResult {
  signups: DashboardRecentSignup[];
  total: number;
}

// --- Unified Calendar ---

export interface UnifiedCalendarItem {
  id: string;
  type: 'shift' | 'event' | 'poll' | 'ticketed_event';
  title: string;
  date: string;
  startTime: string;
  endTime: string;
  location: string | null;
  tags: string[];
  shiftId?: string;
  maxVolunteers?: number | null;
  currentVolunteers?: number;
  gancioEventId?: number;
  gancioUrl?: string;
  pollId?: string;
  pollSlug?: string;
  pollStatus?: SchedulingPollStatus;
  pollVoteCount?: number;
  ticketedEventId?: string;
  eventSlug?: string;
  eventFormat?: string;
  hasPaidTiers?: boolean;
  isSoldOut?: boolean;
  maxAttendees?: number | null;
  currentAttendees?: number;
}

export interface UnifiedCalendarResponse {
  dates: Record<string, { count: number; items: UnifiedCalendarItem[] }>;
}

// --- Map Events (Gancio) ---

export interface MapEvent {
  id: number;
  title: string;
  description: string;
  placeName: string;
  placeAddress: string;
  latitude: number;
  longitude: number;
  startDatetime: string;
  endDatetime: string | null;
  tags: string[];
  shiftId: string | null;
}

// --- People CRM ---

export type ContactSource = 'USER' | 'ADDRESS_OCCUPANT' | 'CAMPAIGN_SENDER' | 'SHIFT_SIGNUP' | 'SMS_CONTACT' | 'DONATION' | 'MANUAL';

export type ConnectionType = 'HOUSEHOLD' | 'FAMILY' | 'COLLEAGUE' | 'REFERRED_BY' | 'CUSTOM';

export type ContactActivityType =
  | 'EMAIL_SENT' | 'RESPONSE_SUBMITTED' | 'SHIFT_SIGNUP' | 'CANVASS_VISIT'
  | 'DONATION' | 'PURCHASE' | 'SMS_SENT' | 'SMS_RECEIVED'
  | 'VIDEO_VIEW' | 'NOTE_ADDED' | 'CONTACT_MERGED'
  | 'PROFILE_SELF_EDIT' | 'PROFILE_PHOTO_UPDATED' | 'USER_LOGIN';

export interface UnifiedPerson {
  id: string; // "user:{id}" | "addr:{id}" | "contact:{id}"
  displayName: string;
  email: string | null;
  phone: string | null;
  source: ContactSource;
  supportLevel: SupportLevel | null;
  lastActivity: string | null;
  isManaged: boolean;
  contactId: string | null;
  userId: string | null;
  addressId: string | null;
  tags: string[];
  engagementScore: number | null;
  userStatus?: string;
  userRole?: string;
}

export interface PeopleListResponse {
  people: UnifiedPerson[];
  pagination: PaginationMeta;
}

export interface PeopleListParams {
  page?: number;
  limit?: number;
  search?: string;
  source?: ContactSource;
  supportLevel?: SupportLevel;
  tag?: string;
  managedOnly?: boolean;
}

export interface PeopleStats {
  total: number;
  bySource: Record<ContactSource, number>;
  managed: number;
  withEmail: number;
  withPhone: number;
}

export interface Contact {
  id: string;
  displayName: string;
  firstName: string | null;
  lastName: string | null;
  email: string | null;
  phone: string | null;
  tags: string[];
  notes: string | null;
  supportLevel: SupportLevel | null;
  signRequested: boolean;
  emailOptOut: boolean;
  smsOptOut: boolean;
  doNotContact: boolean;
  profileToken: string | null;
  profileTokenExpiresAt: string | null;
  hasProfilePassword: boolean;
  coverPhotoPath: string | null;
  lastSelfEditAt: string | null;
  primarySource: ContactSource;
  userId: string | null;
  mergedIntoId: string | null;
  createdByUserId: string | null;
  createdAt: string;
  updatedAt: string;
  emails?: ContactEmailLink[];
  phones?: ContactPhoneLink[];
}

export interface ContactEmailLink {
  id: string;
  contactId: string;
  email: string;
  label: string | null;
  isPrimary: boolean;
  createdAt: string;
}

export interface ContactPhoneLink {
  id: string;
  contactId: string;
  phone: string;
  label: string | null;
  isPrimary: boolean;
  createdAt: string;
}

export interface ContactConnection {
  id: string;
  fromContactId: string;
  toContactId: string;
  type: ConnectionType;
  label: string | null;
  notes: string | null;
  isBidirectional: boolean;
  createdAt: string;
  fromContact?: Contact;
  toContact?: Contact;
}

export interface ContactActivityItem {
  id: string;
  contactId: string;
  type: ContactActivityType;
  title: string;
  description: string | null;
  metadata: Record<string, unknown> | null;
  occurredAt: string;
}

export interface ContactActivityResponse {
  activities: ContactActivityItem[];
  pagination: PaginationMeta;
}

export interface EngagementSummary {
  influence: {
    emailsSent: number;
    responsesSubmitted: number;
    campaignsParticipated: string[];
  };
  map: {
    shiftsSignedUp: number;
    canvassVisits: number;
    canvassSessions: number;
  };
  payments: {
    totalDonatedCAD: number;
    donationCount: number;
  };
  sms: {
    conversations: number;
    messagesReceived: number;
  };
  media: {
    videoViews: number;
  };
  overall: {
    firstSeen: string | null;
    lastSeen: string | null;
    engagementScore: number;
  };
}

export interface PersonDetail {
  person: UnifiedPerson;
  contact: Contact | null;
  engagement: EngagementSummary;
}

export interface ManagePersonPayload {
  sourceType: 'user' | 'addr' | 'contact';
  sourceId: string;
  displayName?: string;
  firstName?: string;
  lastName?: string;
  email?: string;
  phone?: string;
}

export interface UpdateContactPayload {
  displayName?: string;
  firstName?: string;
  lastName?: string;
  email?: string;
  phone?: string;
  tags?: string[];
  notes?: string;
  supportLevel?: SupportLevel | null;
  signRequested?: boolean;
  emailOptOut?: boolean;
  smsOptOut?: boolean;
  doNotContact?: boolean;
}

export interface CreateConnectionPayload {
  toPersonType: 'user' | 'addr' | 'contact';
  toPersonId: string;
  type: ConnectionType;
  label?: string;
  notes?: string;
  isBidirectional?: boolean;
}

export interface MergeContactPayload {
  sourceType: 'user' | 'addr' | 'contact';
  sourceId: string;
  keepFields?: Record<string, 'source' | 'target'>;
}

export interface GraphData {
  nodes: GraphNode[];
  edges: GraphEdge[];
}

export interface GraphNode {
  id: string;
  contactId: string | null;
  displayName: string;
  email: string | null;
  source: ContactSource;
  supportLevel: SupportLevel | null;
  engagementScore: number | null;
}

export interface GraphEdge {
  id: string;
  source: string;
  target: string;
  type: ConnectionType;
  label: string | null;
  isBidirectional: boolean;
}

export interface DuplicatePair {
  personA: UnifiedPerson;
  personB: UnifiedPerson;
  matchType: 'email' | 'phone' | 'name';
  confidence: number;
}

export interface HouseholdMember {
  person: UnifiedPerson;
  unitNumber: string | null;
  isPrimary: boolean;
}

export const CONTACT_SOURCE_LABELS: Record<ContactSource, string> = {
  USER: 'User',
  ADDRESS_OCCUPANT: 'Address',
  CAMPAIGN_SENDER: 'Campaign',
  SHIFT_SIGNUP: 'Shift Signup',
  SMS_CONTACT: 'SMS',
  DONATION: 'Donor',
  MANUAL: 'Manual',
};

export const CONTACT_SOURCE_COLORS: Record<ContactSource, string> = {
  USER: 'blue',
  ADDRESS_OCCUPANT: 'green',
  CAMPAIGN_SENDER: 'purple',
  SHIFT_SIGNUP: 'cyan',
  SMS_CONTACT: 'orange',
  DONATION: 'gold',
  MANUAL: 'default',
};

export const CONNECTION_TYPE_LABELS: Record<ConnectionType, string> = {
  HOUSEHOLD: 'Household',
  FAMILY: 'Family',
  COLLEAGUE: 'Colleague',
  REFERRED_BY: 'Referred By',
  CUSTOM: 'Custom',
};

export const CONNECTION_TYPE_COLORS: Record<ConnectionType, string> = {
  HOUSEHOLD: 'green',
  FAMILY: 'blue',
  COLLEAGUE: 'purple',
  REFERRED_BY: 'orange',
  CUSTOM: 'default',
};

// --- Contact Self-Service Profile ---

export interface ContactProfile {
  id: string;
  displayName: string;
  firstName: string | null;
  lastName: string | null;
  email: string | null;
  phone: string | null;
  primaryAddress: string | null;
  tags: string[];
  supportLevel: SupportLevel | null;
  primarySource: ContactSource;
  emailOptOut: boolean;
  smsOptOut: boolean;
  coverPhotoPath: boolean; // Boolean — has cover photo or not
  engagementScore: number;
  engagement: {
    score: number;
    emailsSent: number;
    responsesSubmitted: number;
    shiftsSignedUp: number;
    canvassVisits: number;
    donationCount: number;
    videoViews: number;
  };
  branding: {
    organizationName: string;
    organizationShortName: string;
    organizationLogoUrl: string | null;
    publicColorPrimary: string;
    publicColorBgBase: string;
    publicColorBgContainer: string;
    publicHeaderGradient: string;
  };
  /** True when the authenticated viewer's User account is linked to this Contact */
  isOwnProfile?: boolean;
  /** Whether social features are enabled platform-wide */
  enableSocial?: boolean;
}

export interface ProfileLinkResponse {
  token: string;
  url: string;
  expiresAt: string | null;
  hasPassword: boolean;
}

// --- People + Users Integration ---

export interface CreateUserFromContactPayload {
  password: string;
  role?: UserRole;
  sendWelcomeEmail?: boolean;
}

export interface CreateUserFromContactResponse {
  user: User;
  action: 'created' | 'linked';
  contactId: string;
}

export interface LinkedContactResponse {
  contact: (Contact & { profileToken: string | null; coverPhotoPath: string | null }) | null;
}

// --- Contact Addresses (Map Integration) ---

export interface ContactAddressLink {
  id: string;
  contactId: string;
  addressId: string;
  isPrimary: boolean;
  createdAt: string;
  address: {
    id: string;
    unitNumber: string | null;
    firstName: string | null;
    lastName: string | null;
    location: {
      id: string;
      address: string;
      latitude: string;
      longitude: string;
      postalCode: string | null;
      province: string | null;
      geocodeConfidence: number | null;
    };
  };
}

export interface AddContactAddressPayload {
  address: string;
  unitNumber?: string;
  isPrimary?: boolean;
  addToMap?: boolean;
}

// --- CRM Tags ---

export interface CrmTag {
  id: string;
  name: string;
  slug: string;
  description: string | null;
  color: string | null;
  listmonkListId: number | null;
  contactCount: number;
  createdAt: string;
  updatedAt: string;
}

export interface CreateCrmTagPayload {
  name: string;
  description?: string;
  color?: string;
  syncToListmonk?: boolean;
}

export interface UpdateCrmTagPayload {
  name?: string;
  description?: string | null;
  color?: string | null;
}

export interface BulkTagPayload {
  tagName: string;
  contactIds: string[];
}

export interface UnregisteredTag {
  name: string;
  count: number;
}

// --- Service Activity Dashboard ---

export interface GiteaActivityData {
  repos: number;
  users: number;
  recentCommits: Array<{
    repo: string;
    message: string;
    author: string;
    date: string;
  }>;
  error?: string;
}

export interface RocketChatStatsData {
  totalUsers: number;
  onlineUsers: number;
  channels: number;
  totalMessages: number;
  topChannels: Array<{ name: string; messages: number; users: number }>;
  error?: string;
}

export interface VaultwardenAdoptionData {
  total: number;
  enabled: number;
  invited: number;
  disabled: number;
  adoptionRate: number;
  error?: string;
}

export interface ListmonkCampaignsData {
  campaigns: Array<{
    name: string;
    status: string;
    sentCount: number;
    openRate: number;
    clickRate: number;
  }>;
  error?: string;
}

// --- Scheduling Polls (Meeting Planner) ---

export type SchedulingPollStatus = 'OPEN' | 'CLOSED' | 'FINALIZED' | 'CANCELLED';
export type PollVoteValue = 'YES' | 'IF_NEED_BE' | 'NO';

export const POLL_STATUS_COLORS: Record<SchedulingPollStatus, string> = {
  OPEN: 'green',
  CLOSED: 'orange',
  FINALIZED: 'blue',
  CANCELLED: 'red',
};

export const POLL_STATUS_LABELS: Record<SchedulingPollStatus, string> = {
  OPEN: 'Open',
  CLOSED: 'Closed',
  FINALIZED: 'Finalized',
  CANCELLED: 'Cancelled',
};

export const VOTE_VALUE_COLORS: Record<PollVoteValue, string> = {
  YES: '#52c41a',
  IF_NEED_BE: '#faad14',
  NO: '#d9d9d9',
};

export const VOTE_VALUE_LABELS: Record<PollVoteValue, string> = {
  YES: 'Yes',
  IF_NEED_BE: 'If Need Be',
  NO: 'No',
};

export interface SchedulingPollOption {
  id: string;
  pollId: string;
  date: string;       // YYYY-MM-DD
  startTime: string;  // HH:MM
  endTime: string;    // HH:MM
  sortOrder: number;
  votes?: SchedulingPollVote[];
  _count?: { votes: number };
  // Aggregated vote counts (from API)
  yesCount?: number;
  ifNeedBeCount?: number;
  noCount?: number;
  score?: number;
}

export interface SchedulingPollVote {
  id: string;
  pollId: string;
  optionId: string;
  userId: string | null;
  contactId: string | null;
  voterName: string;
  voterEmail: string | null;
  voterToken: string | null;
  value: PollVoteValue;
  createdAt: string;
  updatedAt: string;
}

export interface SchedulingPollComment {
  id: string;
  pollId: string;
  userId: string | null;
  authorName: string;
  content: string;
  createdAt: string;
}

export interface SchedulingPoll {
  id: string;
  slug: string;
  title: string;
  description: string | null;
  location: string | null;
  status: SchedulingPollStatus;
  timezone: string;
  finalizedOptionId: string | null;
  finalizedOption: SchedulingPollOption | null;
  convertedShiftId: string | null;
  convertedGancioEventId: number | null;
  convertedCalendarItemId: string | null;
  votingDeadline: string | null;
  autoFinalize: boolean;
  autoFinalizeThreshold: number | null;
  autoConvertToCalendar: boolean;
  autoConvertToGancio: boolean;
  autoConvertToShift: boolean;
  tieBreaker: 'earliest' | 'organizer_choice';
  autoEnrollVoters: boolean;
  autoFinalizeJobId: string | null;
  allowAnonymous: boolean;
  isPrivate: boolean;
  notifyOnVote: boolean;
  requiresAuth?: boolean;
  createdByUserId: string;
  createdBy?: { id: string; name: string | null; email: string };
  createdAt: string;
  updatedAt: string;
  options?: SchedulingPollOption[];
  votes?: SchedulingPollVote[];
  comments?: SchedulingPollComment[];
  _count?: { options: number; votes: number; comments: number };
}

export interface PollsListResponse {
  polls: SchedulingPoll[];
  pagination: PaginationMeta;
}

export interface PollDetailResponse extends SchedulingPoll {
  options: SchedulingPollOption[];
  comments: SchedulingPollComment[];
  voters: Array<{
    name: string;
    voterKey: string;
    votes: Record<string, PollVoteValue>;
  }>;
}

// --- Meeting Agendas & Action Items ---

export type AgendaStatus = 'draft' | 'active' | 'completed';
export type ActionItemStatus = 'open' | 'in_progress' | 'done' | 'blocked';
export type ActionItemPriority = 'low' | 'normal' | 'high' | 'urgent';

export interface AgendaItem {
  id: string;
  title: string;
  durationMinutes?: number;
  presenterUserId?: string;
  notes?: string;
  order: number;
}

export interface MeetingAgenda {
  id: string;
  shiftId: string | null;
  pollId: string | null;
  title: string;
  items: AgendaItem[];
  status: AgendaStatus;
  createdByUserId: string;
  createdBy?: { id: string; name: string | null };
  minutes?: MeetingMinutes | null;
  actionItems?: ActionItem[];
  createdAt: string;
  updatedAt: string;
}

export interface MeetingMinutes {
  id: string;
  agendaId: string;
  notes: string;
  decisions: Array<{ id: string; text: string; passed: boolean }>;
  attendees: Array<{ name: string; pronouns?: string; userId?: string }>;
  approvedAt: string | null;
  approvedByUserId: string | null;
  createdByUserId: string;
  createdAt: string;
  updatedAt: string;
}

export interface ActionItem {
  id: string;
  agendaId: string | null;
  title: string;
  description: string | null;
  assigneeUserId: string | null;
  assignee?: { id: string; name: string | null; email: string } | null;
  dueDate: string | null;
  status: ActionItemStatus;
  priority: ActionItemPriority;
  completedAt: string | null;
  createdByUserId: string;
  createdBy?: { id: string; name: string | null };
  agenda?: { id: string; title: string } | null;
  createdAt: string;
  updatedAt: string;
}

export interface AgendasListResponse {
  agendas: MeetingAgenda[];
  pagination: PaginationMeta;
}

export interface ActionItemsListResponse {
  actionItems: ActionItem[];
  pagination: PaginationMeta;
}

export const ACTION_ITEM_STATUS_COLORS: Record<ActionItemStatus, string> = {
  open: 'blue',
  in_progress: 'orange',
  done: 'green',
  blocked: 'red',
};

export const ACTION_ITEM_STATUS_LABELS: Record<ActionItemStatus, string> = {
  open: 'Open',
  in_progress: 'In Progress',
  done: 'Done',
  blocked: 'Blocked',
};

export const ACTION_ITEM_PRIORITY_COLORS: Record<ActionItemPriority, string> = {
  low: 'default',
  normal: 'blue',
  high: 'orange',
  urgent: 'red',
};

// --- Participant Needs ---

export interface ParticipantNeeds {
  id: string;
  userId: string | null;
  contactId: string | null;
  needsWheelchair: boolean;
  needsGroundFloor: boolean;
  needsHearingLoop: boolean;
  needsSignLanguage: boolean;
  otherAccessibility: string | null;
  isVegan: boolean;
  isVegetarian: boolean;
  isGlutenFree: boolean;
  isHalal: boolean;
  isKosher: boolean;
  hasNutAllergy: boolean;
  otherDietary: string | null;
  needsChildcare: boolean;
  childcareDetails: string | null;
  needsTransportation: boolean;
  transportationNotes: string | null;
  preferredLanguage: string | null;
  needsTranslation: boolean;
  translationLanguage: string | null;
  visibilityConsent: string;
  updatedAt: string;
}

// --- System Upgrade ---

export interface UpgradeChangelogEntry {
  hash: string;
  message: string;
  date: string;
  author: string;
}

export interface UpgradeStatus {
  branch: string;
  currentCommit: string;
  currentCommitFull: string;
  currentMessage: string;
  currentDate: string;
  remoteCommit: string | null;
  remoteCommitFull?: string | null;
  commitsBehind: number;
  changelog: UpgradeChangelogEntry[];
  breaking?: boolean;
  breakingReason?: string;
  checkedAt: string;
  error: string | null;
}

export interface UpgradeProgress {
  phase: number;
  phaseName: string;
  percentage: number;
  message: string;
  lastUpdate: string;
}

export interface UpgradeResult {
  success: boolean;
  message: string;
  previousCommit: string;
  newCommit: string;
  commitCount: number;
  durationSeconds: number;
  warnings: string[];
  completedAt: string;
  triggeredBy?: string;
}

export interface UpgradeHistoryResponse {
  history: UpgradeResult[];
}

export interface WatcherHealth {
  healthy: boolean;
  reason?: string;
  pendingSince?: string;
}

export interface UpgradeStatusResponse {
  status: UpgradeStatus | null;
  progress: UpgradeProgress | null;
  result: UpgradeResult | null;
  running: boolean;
  watcher?: WatcherHealth;
}

// --- Social Calendar Types ---

export type CalendarLayerType = 'SYSTEM' | 'USER' | 'EXTERNAL';
export type CalendarSystemType = 'SHIFTS' | 'TICKETS' | 'POLLS' | 'PUBLIC_EVENTS';
export type CalendarVisibility = 'PRIVATE' | 'FRIENDS' | 'PUBLIC';
export type CalendarItemType = 'EVENT' | 'TIME_BLOCK' | 'REMINDER';
export type CalendarBusyStatus = 'BUSY' | 'TENTATIVE' | 'FREE';
export type CalendarShowDetailsTo = 'NOBODY' | 'FRIENDS' | 'EVERYONE';
export type CalendarRecurrenceFrequency = 'DAILY' | 'WEEKLY' | 'BIWEEKLY' | 'MONTHLY';
export type SeriesEditScope = 'THIS_ONLY' | 'THIS_AND_FUTURE' | 'ALL';

export interface CalendarRecurrenceRule {
  frequency: CalendarRecurrenceFrequency;
  daysOfWeek?: number[];  // 1=Mon...7=Sun
  dayOfMonth?: number;
  interval?: number;
}

export interface CalendarLayer {
  id: string;
  userId: string;
  name: string;
  layerType: CalendarLayerType;
  systemType: CalendarSystemType | null;
  color: string;
  visibility: CalendarVisibility;
  isEnabled: boolean;
  sortOrder: number;
}

export interface CalendarItemData {
  id: string;
  userId: string;
  layerId: string;
  title: string;
  description: string | null;
  date: string;
  startTime: string;
  endTime: string;
  isAllDay: boolean;
  itemType: CalendarItemType;
  location: string | null;
  color: string | null;
  visibility: CalendarVisibility | null;
  busyStatus: CalendarBusyStatus;
  showDetailsTo: CalendarShowDetailsTo;
  recurrenceRule: CalendarRecurrenceRule | null;
  recurrenceEnd: string | null;
  seriesId: string | null;
  isException: boolean;
  sourceType: 'MANUAL' | 'ICS_FEED';
  sourceId: string | null;
}

export interface PersonalCalendarResponse {
  dates: Record<string, { count: number; items: PersonalCalendarItem[] }>;
}

export interface PersonalCalendarItem {
  id: string;
  type: 'shift' | 'event' | 'ticket' | 'poll' | 'personal';
  layerId: string;
  title: string;
  date: string;
  startTime: string;
  endTime: string;
  isAllDay: boolean;
  location: string | null;
  color: string;
  itemType: CalendarItemType;
  busyStatus: CalendarBusyStatus;
  showDetailsTo: CalendarShowDetailsTo;
  // For personal items
  calendarItemId?: string;
  seriesId?: string | null;
  isRecurring?: boolean;
  // For system items
  shiftId?: string;
  ticketId?: string;
  pollId?: string;
}

// --- Shared Calendar Types ---

export type SharedViewType = 'MANUAL' | 'ROLE_BASED';
export type SharedViewScope = 'MEMBERS' | 'PUBLIC';
export type SharedViewMemberStatus = 'INVITED' | 'ACCEPTED' | 'DECLINED';

export interface SharedCalendarView {
  id: string;
  name: string;
  description: string | null;
  ownerId: string;
  viewType: SharedViewType;
  includedLayerTypes: string[];
  shareScope: SharedViewScope;
  shareToken: string | null;
  createdAt: string;
  updatedAt: string;
  owner?: { id: string; name: string | null; email: string };
  _count?: { members: number };
  myStatus?: SharedViewMemberStatus;
}

export interface SharedCalendarMember {
  id: string;
  viewId: string;
  userId: string;
  status: SharedViewMemberStatus;
  color: string;
  joinedAt: string | null;
  user: { id: string; name: string | null; email: string };
}

export interface SharedViewComment {
  id: string;
  viewId: string;
  userId: string;
  itemDate: string;
  itemId: string | null;
  content: string;
  createdAt: string;
  user: { id: string; name: string | null; email: string };
}

export interface SharedViewReactionGroup {
  itemId: string;
  emoji: string;
  count: number;
  users: { id: string; name: string | null }[];
  hasReacted: boolean;
}

export interface SharedCalendarItem extends PersonalCalendarItem {
  memberColor: string;
  memberName: string;
  memberId: string;
}

export interface AvailabilitySlot {
  time: string;
  members: { userId: string; userName: string; status: 'free' | 'busy' | 'tentative' }[];
  allFree: boolean;
}

export interface AvailabilityDay {
  slots: AvailabilitySlot[];
}

export interface AvailabilityResponse {
  dates: Record<string, AvailabilityDay>;
}

// --- Calendar Feed Types ---

export type CalendarFeedStatus = 'OK' | 'ERROR' | 'PENDING';
export type CalendarFeedInterval = 'FIFTEEN_MIN' | 'HOURLY' | 'SIX_HOUR' | 'DAILY';

export interface CalendarFeed {
  id: string;
  userId: string;
  name: string;
  url: string;
  layerId: string;
  refreshInterval: CalendarFeedInterval;
  lastFetchedAt: string | null;
  lastStatus: CalendarFeedStatus;
  lastError: string | null;
  itemCount: number;
  createdAt: string;
  updatedAt: string;
}

// --- Admin Calendar View Types ---

export interface AdminCalendarView {
  id: string;
  name: string;
  description: string | null;
  autoIncludeRoles: string[];
  includedLayerTypes: string[];
  ownerId: string;
  owner: { id: string; name: string | null; email: string };
  userCount: number;
  createdAt: string;
  updatedAt: string;
}

export interface AdminCalendarUser {
  id: string;
  name: string | null;
  email: string;
  role: string;
  color: string;
}

export interface AdminCalendarItem {
  id: string;
  type: string;
  title: string;
  date: string;
  startTime: string;
  endTime: string;
  location: string | null;
  color: string;
  itemType: string;
  layerId: string;
  layerName: string;
  layerColor: string;
  userId: string;
  userName: string;
  userColor: string;
}

export interface CalendarExportToken {
  id: string;
  userId: string;
  token: string;
  includePersonal: boolean;
  includeLayers: string[] | null;
  createdAt: string;
}

// ===== Straw Polls =====

export type StrawPollType = 'SINGLE_CHOICE' | 'YES_NO_ABSTAIN';
export type StrawPollStatus = 'DRAFT' | 'ACTIVE' | 'CLOSED' | 'ARCHIVED';
export type StrawPollIdentityMode = 'ANONYMOUS' | 'TOKEN_GATED' | 'AUTHENTICATED' | 'MIXED';
export type StrawPollResultVisibility = 'LIVE' | 'AFTER_VOTE' | 'AFTER_CLOSE' | 'CREATOR_ONLY' | 'PUBLIC_ALWAYS';

export const STRAW_POLL_STATUS_COLORS: Record<StrawPollStatus, string> = {
  DRAFT: 'default',
  ACTIVE: 'green',
  CLOSED: 'orange',
  ARCHIVED: 'red',
};

export const STRAW_POLL_STATUS_LABELS: Record<StrawPollStatus, string> = {
  DRAFT: 'Draft',
  ACTIVE: 'Active',
  CLOSED: 'Closed',
  ARCHIVED: 'Archived',
};

export const STRAW_POLL_TYPE_LABELS: Record<StrawPollType, string> = {
  SINGLE_CHOICE: 'Single Choice',
  YES_NO_ABSTAIN: 'Yes / No / Abstain',
};

export const STRAW_POLL_IDENTITY_LABELS: Record<StrawPollIdentityMode, string> = {
  ANONYMOUS: 'Anonymous',
  TOKEN_GATED: 'Token-Gated',
  AUTHENTICATED: 'Login Required',
  MIXED: 'Mixed',
};

export const STRAW_POLL_VISIBILITY_LABELS: Record<StrawPollResultVisibility, string> = {
  LIVE: 'Live Results',
  AFTER_VOTE: 'After Voting',
  AFTER_CLOSE: 'After Close',
  CREATOR_ONLY: 'Creator Only',
  PUBLIC_ALWAYS: 'Public Always',
};

export interface StrawPollOption {
  id: string;
  label: string;
  sortOrder: number;
  voteCount?: number;
  _count?: { votes: number };
}

export interface StrawPollVote {
  id: string;
  optionId: string;
  userId: string | null;
  voterName: string | null;
  createdAt: string;
  user?: { id: string; name: string | null; email: string };
}

export interface StrawPollComment {
  id: string;
  authorName: string;
  content: string;
  userId: string | null;
  createdAt: string;
}

export interface StrawPoll {
  id: string;
  slug: string;
  title: string;
  description: string | null;
  type: StrawPollType;
  status: StrawPollStatus;
  identityMode: StrawPollIdentityMode;
  resultVisibility: StrawPollResultVisibility;
  allowComments: boolean;
  closesAt: string | null;
  closeThreshold: number | null;
  autoCloseJobId: string | null;
  isPrivate: boolean;
  createdByUserId: string;
  createdBy?: { id: string; name: string | null; email: string };
  createdAt: string;
  updatedAt: string;
  options?: StrawPollOption[];
  votes?: StrawPollVote[];
  comments?: StrawPollComment[];
  _count?: { votes: number; comments: number; options: number };
  // Public view extras
  totalVotes?: number;
  showResults?: boolean;
  hasVoted?: boolean;
  requiresAuth?: boolean;
}

// --- Petitions ---

export type PetitionStatus = 'DRAFT' | 'ACTIVE' | 'PAUSED' | 'CLOSED' | 'ARCHIVED';
export type PetitionSignatureStatus = 'PENDING_VERIFICATION' | 'VERIFIED' | 'UNVERIFIED' | 'REJECTED';

export interface Petition {
  id: string;
  slug: string;
  title: string;
  description: string | null;
  signatureGoal: number | null;
  showProgress: boolean;
  showSignatureCount: boolean;
  showSignerNames: boolean;
  signatureCountOffset: number;
  requireName: boolean;
  requireEmail: boolean;
  requirePostalCode: boolean;
  requirePhone: boolean;
  allowComment: boolean;
  commentLabel: string | null;
  requireEmailConfirmation: boolean;
  coverPhoto: string | null;
  coverVideoId: number | null;
  callToAction: string | null;
  thankYouMessage: string | null;
  highlightPetition: boolean;
  linkedCampaignId: string | null;
  linkedCampaign?: { id: string; slug: string; title: string; description: string | null; status: string } | null;
  status: PetitionStatus;
  isUserGenerated: boolean;
  moderationStatus: CampaignModerationStatus | null;
  rejectionReason?: string | null;
  moderationNotes?: string | null;
  createdByUserId?: string | null;
  createdByUserEmail?: string | null;
  createdByUserName: string | null;
  reviewedByUserId?: string | null;
  reviewedAt?: string | null;
  createdAt: string;
  updatedAt: string;
  _count: { signatures: number };
}

export interface PetitionSignature {
  id: string;
  petitionId: string;
  signerName: string | null;
  signerEmail: string | null;
  signerPostalCode: string | null;
  signerPhone: string | null;
  signerComment: string | null;
  isAnonymous: boolean;
  displayName: string | null;
  status: PetitionSignatureStatus;
  geoCountry: string | null;
  geoRegion: string | null;
  geoCity: string | null;
  verifiedAt: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface PetitionStats {
  total: number;
  verified: number;
  goal: number | null;
  percentComplete: number | null;
  byCountry: Record<string, number>;
  byRegion: Record<string, number>;
  // recentSigners removed 2026-04-12: see petitions.service.ts for rationale.
}

export interface PetitionsListResponse {
  petitions: Petition[];
  pagination: PaginationMeta;
}

export interface SignPetitionResponse {
  id: string;
  status: PetitionSignatureStatus;
  verificationSent: boolean;
  alreadySigned?: boolean;
}

