import { Logger } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { uuidV7 } from '../common/uuid-v7';
import { withQueryRunner } from './database-migrations.runner';

export async function backfillOrganizations(
  dataSource: DataSource,
): Promise<void> {
  await withQueryRunner(dataSource, async (runner) => {
    const tables = await runner.getTables([
      'tenants',
      'organizations',
      'students',
      'occupancies',
      'classroom_rentals',
    ]);
    const tableNames = new Set(tables.map((table) => table.name));
    if (!tableNames.has('organizations')) return;

    const organizationRows = () =>
      runner.query('SELECT * FROM organizations WHERE is_host = 1 LIMIT 1');
    let host = (await organizationRows())[0];
    if (!host) {
      await runner.query(
        `INSERT INTO organizations (public_id, code, name, is_host, color, notes, status, created_at, updated_at)
           VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
        [
          uuidV7(),
          'HOST',
          process.env.HOST_ORGANIZATION_NAME || '本机构',
          1,
          '#1677ff',
          '系统默认运营主体',
          'active',
        ],
      );
      host = (await organizationRows())[0];
    }
    if (!host) return;

    if (tableNames.has('tenants')) {
      const legacyTenants: Array<Record<string, unknown>> =
        await runner.query('SELECT * FROM tenants');
      for (const legacy of legacyTenants) {
        const name = String(legacy.name || '').trim();
        if (!name) continue;
        let external = (
          await runner.query('SELECT * FROM organizations WHERE name = ? LIMIT 1', [name])
        )[0];
        if (!external) {
          await runner.query(
            `INSERT INTO organizations (public_id, code, name, is_host, contact_name, phone, color, notes, status, created_at, updated_at)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
            [
              uuidV7(),
              `ORG_${String(legacy.id)}`,
              name,
              0,
              legacy.contact || null,
              legacy.phone || null,
              legacy.color || null,
              legacy.notes || null,
              legacy.status || 'active',
            ],
          );
          external = (
            await runner.query('SELECT * FROM organizations WHERE name = ? LIMIT 1', [name])
          )[0];
        }
        if (!external) continue;
        if (tableNames.has('students')) {
          await runner
            .query(
              'UPDATE students SET organization_id = ? WHERE organization_id IS NULL AND tenant_id = ?',
              [external.id, legacy.id],
            )
            .catch(() => undefined);
        }
        if (tableNames.has('occupancies')) {
          await runner
            .query(
              'UPDATE occupancies SET responsible_organization_id = ? WHERE responsible_organization_id IS NULL AND tenant_id = ?',
              [external.id, legacy.id],
            )
            .catch(() => undefined);
        }
        if (tableNames.has('classroom_rentals')) {
          await runner
            .query(
              'UPDATE classroom_rentals SET lessee_organization_id = ?, lessor_organization_id = ? WHERE lessee_organization_id IS NULL AND tenant_id = ?',
              [external.id, host.id, legacy.id],
            )
            .catch(() => undefined);
        }
      }
    }

    if (tableNames.has('students')) {
      await runner.query(
        'UPDATE students SET organization_id = ? WHERE organization_id IS NULL',
        [host.id],
      );
    }
    if (tableNames.has('occupancies')) {
      await runner.query(
        `UPDATE occupancies
           SET responsible_organization_id = COALESCE(
             (SELECT organization_id FROM students WHERE students.id = occupancies.student_id), ?
           )
           WHERE responsible_organization_id IS NULL`,
        [host.id],
      );
    }
    if (tableNames.has('classroom_rentals')) {
      await runner.query(
        'UPDATE classroom_rentals SET lessor_organization_id = ? WHERE lessor_organization_id IS NULL',
        [host.id],
      );
    }
  });
}

export async function normalizeClassDates(
  dataSource: DataSource,
  logger: Logger,
): Promise<void> {
  let columns: Array<'start_date' | 'end_date'> = ['start_date', 'end_date'];

  await withQueryRunner(dataSource, async (runner) => {
    const table = await runner.getTable('classes');
    if (!table) return;

    // Fresh MySQL schemas created by TypeORM already use native DATE columns.
    // This cleanup is only for legacy schemas that stored dates as strings;
    // comparing a native DATE column with '' raises ER_TRUNCATED_WRONG_VALUE
    // in strict SQL mode.
    columns = columns.filter((columnName) => {
      const column = table.columns.find((item) => item.name === columnName);
      const type = String(column?.type ?? '').toLowerCase();
      return !['date', 'datetime', 'timestamp'].includes(type);
    });
  });
  if (columns.length === 0) return;

  const columnText = (column: string) => `CAST(${column} AS CHAR)`;
  const firstTenChars = (column: string) => `NULLIF(LEFT(${columnText(column)}, 10), '')`;
  const normalizedDate = (column: string) => `CASE
            WHEN ${column} IS NULL THEN NULL
            ELSE ${firstTenChars(column)}
          END`;
  const lengthFunction = 'CHAR_LENGTH';
  const needsNormalization = (column: string) => `(
            ${column} IS NOT NULL
            AND (${columnText(column)} = '' OR ${lengthFunction}(${columnText(column)}) > 10)
          )`;

  const assignments = columns
    .map((column) => `${column} = ${normalizedDate(column)}`)
    .join(',\n          ');
  const predicates = columns.map((column) => needsNormalization(column)).join('\n          OR ');
  const result = await dataSource.transaction((manager) =>
    manager.query(`
        UPDATE classes
        SET
          ${assignments}
        WHERE
          ${predicates}
      `),
  );

  const affected = typeof result?.changes === 'number' ? result.changes : result?.affectedRows;
  if (affected) logger.log(`已规范化 ${affected} 条班级日期数据`);
}
