import { MigrationInterface, QueryRunner } from 'typeorm';

interface LegacyServerRow {
  id: string;
  channels: string | null;
}

interface LegacyServerChannel {
  id: string;
  name: string;
  type: 'text' | 'voice';
  position: number;
}

function normalizeLegacyChannels(raw: string | null): LegacyServerChannel[] {
  try {
    const parsed = JSON.parse(raw || '[]');

    if (!Array.isArray(parsed)) {
      return [];
    }

    const seenIds = new Set<string>();
    const seenNames = new Set<string>();

    return parsed
      .filter((channel): channel is Record<string, unknown> => !!channel && typeof channel === 'object')
      .map((channel, index) => {
        const id = typeof channel.id === 'string' ? channel.id.trim() : '';
        const name = typeof channel.name === 'string' ? channel.name.trim().replace(/\s+/g, ' ') : '';
        const type = channel.type === 'text' || channel.type === 'voice' ? channel.type : null;
        const position = typeof channel.position === 'number' ? channel.position : index;
        const nameKey = type ? `${type}:${name.toLocaleLowerCase()}` : '';

        if (!id || !name || !type || seenIds.has(id) || seenNames.has(nameKey)) {
          return null;
        }

        seenIds.add(id);
        seenNames.add(nameKey);

        return {
          id,
          name,
          type,
          position
        } satisfies LegacyServerChannel;
      })
      .filter((channel): channel is LegacyServerChannel => !!channel);
  } catch {
    return [];
  }
}

function shouldRestoreLegacyVoiceGeneral(channels: LegacyServerChannel[]): boolean {
  const hasTextGeneral = channels.some(
    (channel) => channel.type === 'text' && (channel.id === 'general' || channel.name.toLocaleLowerCase() === 'general')
  );
  const hasVoiceAfk = channels.some(
    (channel) => channel.type === 'voice' && (channel.id === 'vc-afk' || channel.name.toLocaleLowerCase() === 'afk')
  );
  const hasVoiceGeneral = channels.some(
    (channel) => channel.type === 'voice' && (channel.id === 'vc-general' || channel.name.toLocaleLowerCase() === 'general')
  );

  return hasTextGeneral && hasVoiceAfk && !hasVoiceGeneral;
}

function repairLegacyVoiceChannels(channels: LegacyServerChannel[]): LegacyServerChannel[] {
  if (!shouldRestoreLegacyVoiceGeneral(channels)) {
    return channels;
  }

  const textChannels = channels.filter((channel) => channel.type === 'text');
  const voiceChannels = channels.filter((channel) => channel.type === 'voice');
  const repairedVoiceChannels = [
    {
      id: 'vc-general',
      name: 'General',
      type: 'voice' as const,
      position: 0
    },
    ...voiceChannels
  ].map((channel, index) => ({
    ...channel,
    position: index
  }));

  return [
    ...textChannels,
    ...repairedVoiceChannels
  ];
}

export class RepairLegacyVoiceChannels1000000000003 implements MigrationInterface {
  name = 'RepairLegacyVoiceChannels1000000000003';

  public async up(queryRunner: QueryRunner): Promise<void> {
    const rows = await queryRunner.query(`SELECT "id", "channels" FROM "servers"`) as LegacyServerRow[];

    for (const row of rows) {
      const channels = normalizeLegacyChannels(row.channels);
      const repaired = repairLegacyVoiceChannels(channels);

      if (JSON.stringify(repaired) === JSON.stringify(channels)) {
        continue;
      }

      await queryRunner.query(
        `UPDATE "servers" SET "channels" = ? WHERE "id" = ?`,
        [JSON.stringify(repaired), row.id]
      );
    }
  }

  public async down(_queryRunner: QueryRunner): Promise<void> {
    // Forward-only data repair migration.
  }
}
