import {
  Column,
  DeleteDateColumn,
  Entity,
  Index,
  JoinColumn,
  ManyToOne,
  PrimaryGeneratedColumn,
} from 'typeorm';

import { User } from '../../user/entities/user.entity';

// ─── ENUMS ───────────────────────────────────────────────────────────────
// 'Converted' is reachable only via a dedicated convert-to-client flow —
// never set directly through create/update/bulk-update (mirrors the
// Laravel controller, which excludes it from those status option lists).

export enum LeadStatus {
  NEW = 'New',
  INTERESTED = 'Interested',
  CONVERTED = 'Converted',
  LOST = 'Lost',
}

export enum LeadPriority {
  LOW = 'Low',
  MEDIUM = 'Medium',
  HIGH = 'High',
}

// ─── CLIENT GROUP ────────────────────────────────────────────────────────
// Four values are STORABLE, only three are SELECTABLE.
//
// QRS_B is a legacy value: rows already in the database may carry it, and
// the column must therefore accept it, but nobody picks it on a form any
// more. Everywhere the app groups, filters, or reports, QRS_B rolls up
// into QRS — so filtering the list by "QRS" returns QRS_B rows too, and
// the analytics "QRS" bucket counts them.
//
// If you later decide QRS_B should be pickable again, add it to
// SELECTABLE_CLIENT_GROUPS below and remove its entry from
// CLIENT_GROUP_ROLLUP. Nothing else needs to change.

export enum ClientGroup {
  QRS = 'QRS',
  TQS = 'TQS',
  QRS_B = 'QRS_B',
  QRS_NEW = 'QRS_NEW',
}

/** Offered in the dropdown. QRS_B is deliberately absent — it is legacy-only. */
export const SELECTABLE_CLIENT_GROUPS = [
  ClientGroup.QRS,
  ClientGroup.TQS,
  ClientGroup.QRS_NEW,
] as const;

/** What each stored value counts as when grouping / filtering / reporting. */
export const CLIENT_GROUP_ROLLUP: Record<ClientGroup, ClientGroup> = {
  [ClientGroup.QRS]: ClientGroup.QRS,
  [ClientGroup.QRS_B]: ClientGroup.QRS, // ← the rule you asked for
  [ClientGroup.TQS]: ClientGroup.TQS,
  [ClientGroup.QRS_NEW]: ClientGroup.QRS_NEW,
};

/** Human labels for API responses and the frontend dropdown. */
export const CLIENT_GROUP_LABELS: Record<ClientGroup, string> = {
  [ClientGroup.QRS]: 'QRS',
  [ClientGroup.QRS_B]: 'QRS (legacy B)',
  [ClientGroup.TQS]: 'TQS',
  [ClientGroup.QRS_NEW]: 'QRS New',
};

/** QRS_B → QRS. Anything unrecognised → null. */
export function normalizeClientGroup(
  value: string | null | undefined,
): ClientGroup | null {
  if (!value) return null;
  const key = value.toUpperCase().trim() as ClientGroup;
  return CLIENT_GROUP_ROLLUP[key] ?? null;
}

/**
 * The stored values a filter should match. Filtering by QRS must also
 * return QRS_B rows, so this returns ['QRS', 'QRS_B'] for QRS.
 */
export function expandClientGroup(value: string): ClientGroup[] {
  const target = normalizeClientGroup(value);
  if (!target) return [];
  return (Object.keys(CLIENT_GROUP_ROLLUP) as ClientGroup[]).filter(
    (stored) => CLIENT_GROUP_ROLLUP[stored] === target,
  );
}

// Adjust to your actual allowed source values.
export const LEAD_SOURCES = [
  'Website',
  'Referral',
  'Cold Call',
  'Email Campaign',
  'Trade Show',
  'Social Media',
  'Other',
] as const;
export type LeadSource = (typeof LEAD_SOURCES)[number];

// Adjust to your actual allowed lost-reason values.
export const LEAD_LOST_REASONS = [
  'Price',
  'Went with competitor',
  'No budget',
  'No response',
  'Not a fit',
  'Other',
] as const;
export type LeadLostReason = (typeof LEAD_LOST_REASONS)[number];

// ─── TRANSFORMERS ────────────────────────────────────────────────────────
// standards/tags are stored as longtext (JSON-encoded arrays), matching the
// exact column type in your existing table (not a native JSON column).

const jsonArrayTransformer = {
  to: (value: string[] | null): string | null =>
    value && value.length ? JSON.stringify(value) : null,
  from: (value: string | null): string[] | null => {
    if (!value) return null;
    try {
      const parsed = JSON.parse(value);
      return Array.isArray(parsed) ? parsed : null;
    } catch {
      return null;
    }
  },
};

// ─── ENTITY ──────────────────────────────────────────────────────────────

@Entity('leads')
@Index(['status'])
@Index(['assigned_to'])
@Index(['created_by'])
@Index(['email'])
@Index(['company'])
@Index(['client_group'])
export class Lead {
  @PrimaryGeneratedColumn({ type: 'bigint', unsigned: true })
  id: number;

  @Column({ type: 'varchar', length: 255, unique: true })
  lead_code: string;

  @Column({ type: 'varchar', length: 255 })
  company: string;

  // 🆕 Nullable so every pre-existing row stays valid without a backfill.
  @Column({
    type: 'enum',
    enum: ClientGroup,
    nullable: true,
  })
  client_group: ClientGroup | null;

  @Column({ type: 'varchar', length: 255, nullable: true })
  contact: string | null;

  @Column({ type: 'varchar', length: 255, nullable: true })
  phone: string | null;

  @Column({ type: 'varchar', length: 255, nullable: true })
  email: string | null;

  @Column({ type: 'varchar', length: 255, nullable: true })
  website: string | null;

  @Column({
    type: 'longtext',
    nullable: true,
    transformer: jsonArrayTransformer,
  })
  standards: string[] | null;

  @Column({
    type: 'enum',
    enum: LeadStatus,
    default: LeadStatus.NEW,
  })
  status: LeadStatus;

  @Column({ type: 'varchar', length: 255, nullable: true })
  lost_reason: string | null;

  @Column({ type: 'text', nullable: true })
  lost_notes: string | null;

  @Column({ type: 'timestamp', nullable: true })
  lost_at: Date | null;

  @Column({ type: 'varchar', length: 255, nullable: true })
  source: string | null;

  @Column({
    type: 'enum',
    enum: LeadPriority,
    default: LeadPriority.MEDIUM,
  })
  priority: LeadPriority;

  @Column({ type: 'text', nullable: true })
  notes: string | null;

  @Column({
    type: 'longtext',
    nullable: true,
    transformer: jsonArrayTransformer,
  })
  tags: string[] | null;

  @Column({ type: 'int', nullable: true })
  assigned_to: number | null;

  @ManyToOne(() => User, { nullable: true })
  @JoinColumn({ name: 'assigned_to' })
  assignedUser: User | null;

  // 🆕 Audit trail for the reassignment notification — who handed this over,
  // and when. Read by the frontend to show "Reassigned 2h ago by …".
  @Column({ type: 'int', nullable: true })
  assigned_by: number | null;

  @ManyToOne(() => User, { nullable: true })
  @JoinColumn({ name: 'assigned_by' })
  assignedByUser: User | null;

  @Column({ type: 'timestamp', nullable: true })
  assigned_at: Date | null;

  @Column({ type: 'int', nullable: true })
  created_by: number | null;

  @ManyToOne(() => User, { nullable: true })
  @JoinColumn({ name: 'created_by' })
  creator: User | null;

  @Column({ type: 'timestamp', nullable: true })
  converted_at: Date | null;

  // Nullable, no DB default — matches your table exactly. Set manually in
  // the service (same as Laravel's app-managed timestamps), not via
  // @CreateDateColumn/@UpdateDateColumn (which would add DB defaults).
  @Column({ type: 'timestamp', nullable: true })
  created_at: Date | null;

  @Column({ type: 'timestamp', nullable: true })
  updated_at: Date | null;

  @DeleteDateColumn({ type: 'timestamp', nullable: true })
  deleted_at: Date | null;

  /** Convenience for API consumers — QRS_B reads back as QRS. */
  get client_group_normalized(): ClientGroup | null {
    return normalizeClientGroup(this.client_group);
  }
}
