/* eslint-disable @typescript-eslint/no-explicit-any */
import type {
  Campaign,
  CampaignStats,
  ComplianceStore,
  ConsentRecord,
  MarketingContact,
  MarketingList,
  SuppressionEntry,
} from '@platform-modules/marketing'
import type { Segment, SegmentFilter, SelfManagedStore } from '@platform-modules/marketing/self-managed'
import type { Db, DbTx } from '@zync/db'
import {
  listCampaignRowsForTenant,
  listLeadRowsForTenant,
  upsertCampaignRow,
  upsertLeadRow,
  type UpsertCampaignRowInput,
  type UpsertLeadRowInput,
} from '@zync/db/queries'

type DbLike = Db | DbTx

type PlatformMarketingState = {
  sequence?: number
  leads: Array<Record<string, any>>
  campaigns: Array<Record<string, any>>
}

type StateDb = {
  __platformMarketingState?: PlatformMarketingState
}

type PlatformMarketingMeta = {
  attributes?: Record<string, string>
  tags?: string[]
  listIds?: string[]
  consent?: ConsentRecord
  suppression?: SuppressionEntry
}

type CampaignPayload =
  | {
      kind: 'platform-list'
    }
  | {
      kind: 'platform-segment'
      listId: string
      filter: SegmentFilter
    }
  | {
      kind: 'platform-campaign'
      subject: string
      html: string
      text?: string
      listIds: string[]
      region?: string
      stats: CampaignStats
    }

function normalizeEmail(email: string): string {
  return email.trim().toLowerCase()
}

function getState(db: DbLike): PlatformMarketingState | null {
  return (db as StateDb).__platformMarketingState ?? null
}

function nextId(state: PlatformMarketingState, prefix: string): string {
  const current = state.sequence ?? 1
  state.sequence = current + 1
  return `${prefix}-${current}`
}

function getMarketingMeta(row: Record<string, any>): PlatformMarketingMeta {
  const sourceMetadata = row.sourceMetadata
  if (!sourceMetadata || typeof sourceMetadata !== 'object') {
    return {}
  }

  const platformMarketing = sourceMetadata.platformMarketing
  if (!platformMarketing || typeof platformMarketing !== 'object') {
    return {}
  }

  return platformMarketing as PlatformMarketingMeta
}

function setMarketingMeta(row: Record<string, any>, meta: PlatformMarketingMeta): Record<string, any> {
  return {
    ...row,
    sourceMetadata: {
      ...(row.sourceMetadata && typeof row.sourceMetadata === 'object' ? row.sourceMetadata : {}),
      platformMarketing: meta,
    },
  }
}

function baseStats(): CampaignStats {
  return {
    opens: 0,
    clicks: 0,
    bounces: 0,
    unsubs: 0,
  }
}

function isListPayload(payload: unknown): payload is Extract<CampaignPayload, { kind: 'platform-list' }> {
  return !!payload && typeof payload === 'object' && (payload as { kind?: string }).kind === 'platform-list'
}

function isSegmentPayload(
  payload: unknown,
): payload is Extract<CampaignPayload, { kind: 'platform-segment' }> {
  return !!payload && typeof payload === 'object' && (payload as { kind?: string }).kind === 'platform-segment'
}

function isCampaignPayload(
  payload: unknown,
): payload is Extract<CampaignPayload, { kind: 'platform-campaign' }> {
  return !!payload && typeof payload === 'object' && (payload as { kind?: string }).kind === 'platform-campaign'
}

function mapList(row: Record<string, any>): MarketingList {
  return {
    id: row.id,
    name: row.name,
  }
}

function mapSegment(row: Record<string, any>): Segment {
  const payload = row.targetSegment as Extract<CampaignPayload, { kind: 'platform-segment' }>
  return {
    id: row.id,
    name: row.name,
    listId: payload.listId,
    filter: payload.filter,
  }
}

function mapCampaign(row: Record<string, any>): Campaign | null {
  const payload = row.targetSegment
  if (!isCampaignPayload(payload)) {
    return null
  }

  return {
    id: row.id,
    name: row.name,
    subject: payload.subject,
    html: payload.html,
    text: payload.text,
    listIds: payload.listIds,
    region: payload.region,
  }
}

async function readLeadRows(db: DbLike, tenantId: string): Promise<Array<Record<string, any>>> {
  const state = getState(db)
  if (state) {
    return state.leads.filter((row) => row.tenantId === tenantId)
  }

  return listLeadRowsForTenant(db, tenantId)
}

async function saveLeadRow(db: DbLike, row: Record<string, any>): Promise<Record<string, any>> {
  const state = getState(db)
  if (state) {
    const index = state.leads.findIndex((candidate) => candidate.id === row.id)
    if (index === -1) {
      state.leads.push(row)
    } else {
      state.leads[index] = row
    }
    return row
  }

  if (typeof row.id !== 'string' || typeof row.tenantId !== 'string' || typeof row.name !== 'string') {
    throw new Error('Invalid lead row')
  }

  const input: UpsertLeadRowInput = {
    id: row.id,
    tenantId: row.tenantId,
    name: row.name,
    email: row.email ?? null,
    stage: row.stage,
    stagePosition: row.stagePosition,
    source: row.source,
    sourceMetadata: row.sourceMetadata ?? null,
    archivedAt: row.archivedAt ?? null,
    createdAt: row.createdAt,
    updatedAt: row.updatedAt,
  }

  return upsertLeadRow(db, input)
}

async function readCampaignRows(db: DbLike, tenantId: string): Promise<Array<Record<string, any>>> {
  const state = getState(db)
  if (state) {
    return state.campaigns.filter((row) => row.tenantId === tenantId)
  }

  return listCampaignRowsForTenant(db, tenantId)
}

async function saveCampaignRow(db: DbLike, row: Record<string, any>): Promise<Record<string, any>> {
  const state = getState(db)
  if (state) {
    const index = state.campaigns.findIndex((candidate) => candidate.id === row.id)
    if (index === -1) {
      state.campaigns.push(row)
    } else {
      state.campaigns[index] = row
    }
    return row
  }

  if (typeof row.id !== 'string' || typeof row.tenantId !== 'string' || typeof row.name !== 'string') {
    throw new Error('Invalid campaign row')
  }

  const input: UpsertCampaignRowInput = {
    id: row.id,
    tenantId: row.tenantId,
    name: row.name,
    type: row.type,
    status: row.status,
    targetSegment: row.targetSegment ?? null,
    scheduledAt: row.scheduledAt ?? null,
    sentCount: row.sentCount,
    isActive: row.isActive,
    createdAt: row.createdAt,
    updatedAt: row.updatedAt,
  }

  return upsertCampaignRow(db, input)
}

async function findLeadByEmail(
  db: DbLike,
  tenantId: string,
  email: string,
): Promise<Record<string, any> | null> {
  const normalized = normalizeEmail(email)
  const rows = await readLeadRows(db, tenantId)
  return rows.find((row) => normalizeEmail(row.email ?? '') === normalized) ?? null
}

async function findCampaignRow(db: DbLike, tenantId: string, id: string): Promise<Record<string, any> | null> {
  const rows = await readCampaignRows(db, tenantId)
  return rows.find((row) => row.id === id) ?? null
}

export function createPlatformComplianceStore(
  db: DbLike,
  tenantId: string,
  now: () => Date = () => new Date(),
): ComplianceStore {
  return {
    async isSuppressed(email: string): Promise<boolean> {
      const lead = await findLeadByEmail(db, tenantId, email)
      return !!getMarketingMeta(lead ?? {}).suppression
    },

    async getConsent(email: string): Promise<ConsentRecord | null> {
      const lead = await findLeadByEmail(db, tenantId, email)
      return getMarketingMeta(lead ?? {}).consent ?? null
    },

    async setConsent(record: ConsentRecord): Promise<void> {
      const normalized = normalizeEmail(record.email)
      const existing =
        (await findLeadByEmail(db, tenantId, normalized)) ?? {
          id: getState(db) ? nextId(getState(db) as PlatformMarketingState, 'lead') : crypto.randomUUID(),
          tenantId,
          name: normalized,
          email: normalized,
          stage: 'NEW',
          stagePosition: '0',
          source: 'manual',
          createdAt: now().toISOString(),
          updatedAt: now().toISOString(),
        }
      const meta = getMarketingMeta(existing)
      await saveLeadRow(
        db,
        setMarketingMeta(
          {
            ...existing,
            email: normalized,
            name: existing.name || normalized,
            updatedAt: now().toISOString(),
          },
          {
            ...meta,
            consent: {
              ...record,
              email: normalized,
            },
          },
        ),
      )
    },

    async addSuppression(entry: SuppressionEntry): Promise<void> {
      const normalized = normalizeEmail(entry.email)
      const existing =
        (await findLeadByEmail(db, tenantId, normalized)) ?? {
          id: getState(db) ? nextId(getState(db) as PlatformMarketingState, 'lead') : crypto.randomUUID(),
          tenantId,
          name: normalized,
          email: normalized,
          stage: 'NEW',
          stagePosition: '0',
          source: 'manual',
          createdAt: now().toISOString(),
          updatedAt: now().toISOString(),
        }
      const meta = getMarketingMeta(existing)
      await saveLeadRow(
        db,
        setMarketingMeta(
          {
            ...existing,
            email: normalized,
            name: existing.name || normalized,
            updatedAt: now().toISOString(),
          },
          {
            ...meta,
            suppression: {
              ...entry,
              email: normalized,
            },
          },
        ),
      )
    },

    async listSuppressed(): Promise<SuppressionEntry[]> {
      const rows = await readLeadRows(db, tenantId)
      return rows
        .map((row) => getMarketingMeta(row).suppression ?? null)
        .filter((entry): entry is SuppressionEntry => !!entry)
        .map((entry) => ({
          ...entry,
          email: normalizeEmail(entry.email),
        }))
    },
  }
}

export function createPlatformSelfManagedStore(
  db: DbLike,
  tenantId: string,
  now: () => Date = () => new Date(),
): SelfManagedStore & {
  createCampaign(campaign: Omit<Campaign, 'id'> & { name: string }): Promise<Campaign>
} {
  async function saveCampaignRecord(row: Record<string, any>): Promise<Record<string, any>> {
    return saveCampaignRow(db, {
      ...row,
      tenantId,
      updatedAt: now().toISOString(),
    })
  }

  return {
    async upsertContact(contact: MarketingContact, listId?: string): Promise<void> {
      const normalized = normalizeEmail(contact.email)
      const existing =
        (await findLeadByEmail(db, tenantId, normalized)) ?? {
          id: getState(db) ? nextId(getState(db) as PlatformMarketingState, 'lead') : crypto.randomUUID(),
          tenantId,
          name: normalized,
          email: normalized,
          stage: 'NEW',
          stagePosition: '0',
          source: 'manual',
          createdAt: now().toISOString(),
        }
      const meta = getMarketingMeta(existing)
      const listIds = new Set(meta.listIds ?? [])
      if (listId) {
        listIds.add(listId)
      }

      await saveLeadRow(
        db,
        setMarketingMeta(
          {
            ...existing,
            email: normalized,
            name: existing.name || normalized,
            updatedAt: now().toISOString(),
          },
          {
            ...meta,
            attributes: {
              ...(meta.attributes ?? {}),
              ...(contact.attributes ?? {}),
            },
            tags: Array.from(new Set([...(meta.tags ?? []), ...(contact.tags ?? [])])),
            listIds: Array.from(listIds),
          },
        ),
      )
    },

    async removeContact(email: string, listId?: string): Promise<void> {
      const existing = await findLeadByEmail(db, tenantId, email)
      if (!existing) {
        return
      }

      const meta = getMarketingMeta(existing)
      if (listId) {
        await saveLeadRow(
          db,
          setMarketingMeta(
            {
              ...existing,
              updatedAt: now().toISOString(),
            },
            {
              ...meta,
              listIds: (meta.listIds ?? []).filter((candidate) => candidate !== listId),
            },
          ),
        )
        return
      }

      await saveLeadRow(db, {
        ...existing,
        archivedAt: now().toISOString(),
        updatedAt: now().toISOString(),
      })
    },

    async tagContact(email: string, tag: string): Promise<void> {
      const existing = await findLeadByEmail(db, tenantId, email)
      if (!existing) {
        return
      }
      const meta = getMarketingMeta(existing)
      await saveLeadRow(
        db,
        setMarketingMeta(
          {
            ...existing,
            updatedAt: now().toISOString(),
          },
          {
            ...meta,
            tags: Array.from(new Set([...(meta.tags ?? []), tag])),
          },
        ),
      )
    },

    async createList(name: string): Promise<MarketingList> {
      const row = await saveCampaignRecord({
        id: getState(db) ? nextId(getState(db) as PlatformMarketingState, 'list') : crypto.randomUUID(),
        name,
        type: 'email',
        status: 'draft',
        targetSegment: { kind: 'platform-list' } satisfies CampaignPayload,
        sentCount: 0,
        isActive: true,
        createdAt: now().toISOString(),
      })
      return mapList(row)
    },

    async lists(): Promise<MarketingList[]> {
      const rows = await readCampaignRows(db, tenantId)
      return rows.filter((row) => isListPayload(row.targetSegment)).map(mapList)
    },

    async saveCampaign(campaign: Campaign): Promise<void> {
      await saveCampaignRecord({
        id: campaign.id,
        name: campaign.name,
        type: 'email',
        status: 'draft',
        targetSegment: {
          kind: 'platform-campaign',
          subject: campaign.subject,
          html: campaign.html,
          text: campaign.text,
          listIds: campaign.listIds,
          region: campaign.region,
          stats: baseStats(),
        } satisfies CampaignPayload,
        sentCount: 0,
        isActive: true,
        createdAt: now().toISOString(),
      })
    },

    async createCampaign(campaign): Promise<Campaign> {
      const saved = {
        ...campaign,
        id: crypto.randomUUID(),
      }
      await this.saveCampaign(saved)
      return saved
    },

    async getCampaign(id: string): Promise<Campaign | null> {
      const row = await findCampaignRow(db, tenantId, id)
      return row ? mapCampaign(row) : null
    },

    async getListRecipients(listIds: string[]): Promise<string[]> {
      const wanted = new Set(listIds)
      const rows = await readLeadRows(db, tenantId)
      return rows
        .filter((row) => !row.archivedAt)
        .filter((row) => (getMarketingMeta(row).listIds ?? []).some((listId) => wanted.has(listId)))
        .map((row) => normalizeEmail(row.email ?? ''))
        .filter((email) => email.length > 0)
    },

    async recordOpen(campaignId: string): Promise<void> {
      const row = await findCampaignRow(db, tenantId, campaignId)
      if (!row || !isCampaignPayload(row.targetSegment)) {
        return
      }
      await saveCampaignRecord({
        ...row,
        targetSegment: {
          ...row.targetSegment,
          stats: {
            ...row.targetSegment.stats,
            opens: row.targetSegment.stats.opens + 1,
          },
        },
      })
    },

    async recordClick(campaignId: string): Promise<void> {
      const row = await findCampaignRow(db, tenantId, campaignId)
      if (!row || !isCampaignPayload(row.targetSegment)) {
        return
      }
      await saveCampaignRecord({
        ...row,
        targetSegment: {
          ...row.targetSegment,
          stats: {
            ...row.targetSegment.stats,
            clicks: row.targetSegment.stats.clicks + 1,
          },
        },
      })
    },

    async getStats(campaignId: string): Promise<CampaignStats> {
      const row = await findCampaignRow(db, tenantId, campaignId)
      if (!row || !isCampaignPayload(row.targetSegment)) {
        return baseStats()
      }
      return row.targetSegment.stats
    },

    async createSegment(def: { name: string; listId: string; filter: SegmentFilter }): Promise<Segment> {
      const row = await saveCampaignRecord({
        id: getState(db) ? nextId(getState(db) as PlatformMarketingState, 'segment') : crypto.randomUUID(),
        name: def.name,
        type: 'email',
        status: 'draft',
        targetSegment: {
          kind: 'platform-segment',
          listId: def.listId,
          filter: def.filter,
        } satisfies CampaignPayload,
        sentCount: 0,
        isActive: true,
        createdAt: now().toISOString(),
      })
      return mapSegment(row)
    },

    async listSegments(listId?: string): Promise<Segment[]> {
      const rows = await readCampaignRows(db, tenantId)
      return rows
        .filter((row): row is Record<string, any> & { targetSegment: Extract<CampaignPayload, { kind: 'platform-segment' }> } => isSegmentPayload(row.targetSegment))
        .filter((row) => !listId || row.targetSegment.listId === listId)
        .map(mapSegment)
    },

    async scheduleCampaign(campaignId: string, scheduledAt: string): Promise<void> {
      const row = await findCampaignRow(db, tenantId, campaignId)
      if (!row) {
        return
      }
      await saveCampaignRecord({
        ...row,
        scheduledAt,
      })
    },

  }
}

export function createPlatformMarketingDeps(
  db: DbLike,
  tenantId: string,
  now: () => Date = () => new Date(),
) {
  return {
    complianceStore: createPlatformComplianceStore(db, tenantId, now),
    store: createPlatformSelfManagedStore(db, tenantId, now),
  }
}
