import { describe, expect, it } from 'vitest';

import {
  DUCKDB_ROWID_LOCATOR_COLUMN,
  ORACLE_ROWID_LOCATOR_COLUMN,
  filterHiddenLocatorColumns,
  resolveEditRowLocator,
  resolveRowLocatorValues,
} from './rowLocator';

const uniqueIndex = (name: string, columnName: string, seqInIndex = 1) => ({
  name,
  columnName,
  seqInIndex,
  nonUnique: 0,
  indexType: 'BTREE',
});

const normalIndex = (name: string, columnName: string, seqInIndex = 1) => ({
  name,
  columnName,
  seqInIndex,
  nonUnique: 1,
  indexType: 'BTREE',
});

describe('resolveEditRowLocator', () => {
  it('prefers primary keys over unique indexes', () => {
    expect(resolveEditRowLocator({
      dbType: 'mysql',
      resultColumns: ['ID', 'EMAIL'],
      primaryKeys: ['ID'],
      indexes: [uniqueIndex('uk_email', 'EMAIL')],
    })).toEqual({
      strategy: 'primary-key',
      columns: ['ID'],
      valueColumns: ['ID'],
      readOnly: false,
    });
  });

  it('uses a unique index when there is no primary key', () => {
    expect(resolveEditRowLocator({
      dbType: 'mysql',
      resultColumns: ['EMAIL', 'NAME'],
      indexes: [uniqueIndex('uk_email', 'EMAIL')],
    })).toEqual({
      strategy: 'unique-key',
      columns: ['EMAIL'],
      valueColumns: ['EMAIL'],
      readOnly: false,
    });
  });

  it('sorts composite unique index columns by sequence', () => {
    expect(resolveEditRowLocator({
      dbType: 'postgres',
      resultColumns: ['TENANT_ID', 'CODE', 'NAME'],
      indexes: [
        uniqueIndex('uk_tenant_code', 'CODE', 2),
        uniqueIndex('uk_tenant_code', 'TENANT_ID', 1),
      ],
    })).toMatchObject({
      strategy: 'unique-key',
      columns: ['TENANT_ID', 'CODE'],
      valueColumns: ['TENANT_ID', 'CODE'],
      readOnly: false,
    });
  });

  it('ignores non-unique indexes', () => {
    expect(resolveEditRowLocator({
      dbType: 'mysql',
      resultColumns: ['NAME'],
      indexes: [normalIndex('idx_name', 'NAME')],
    })).toMatchObject({
      strategy: 'none',
      readOnly: true,
    });
  });

  it('keeps results read-only when primary key columns are missing from result columns', () => {
    expect(resolveEditRowLocator({
      dbType: 'oracle',
      resultColumns: ['NAME'],
      primaryKeys: ['ID'],
    })).toMatchObject({
      strategy: 'none',
      readOnly: true,
      reason: '结果集中缺少主键列 ID，无法安全提交修改。',
    });
  });

  it('uses Oracle ROWID when no primary or unique key is available', () => {
    expect(resolveEditRowLocator({
      dbType: 'oracle',
      resultColumns: ['NAME', ORACLE_ROWID_LOCATOR_COLUMN],
      allowOracleRowID: true,
    })).toEqual({
      strategy: 'oracle-rowid',
      columns: ['ROWID'],
      valueColumns: [ORACLE_ROWID_LOCATOR_COLUMN],
      hiddenColumns: [ORACLE_ROWID_LOCATOR_COLUMN],
      readOnly: false,
    });
  });

  it('uses DuckDB rowid when no primary or unique key is available', () => {
    expect(resolveEditRowLocator({
      dbType: 'duckdb',
      resultColumns: ['name', DUCKDB_ROWID_LOCATOR_COLUMN],
      allowDuckDBRowID: true,
    })).toEqual({
      strategy: 'duckdb-rowid',
      columns: ['rowid'],
      valueColumns: [DUCKDB_ROWID_LOCATOR_COLUMN],
      hiddenColumns: [DUCKDB_ROWID_LOCATOR_COLUMN],
      readOnly: false,
    });
  });
});

describe('resolveRowLocatorValues', () => {
  it('extracts locator values from the original row', () => {
    const locator = resolveEditRowLocator({
      dbType: 'mysql',
      resultColumns: ['EMAIL', 'NAME'],
      indexes: [uniqueIndex('uk_email', 'EMAIL')],
    });

    expect(resolveRowLocatorValues(locator, { EMAIL: 'a@example.com', NAME: 'A' })).toEqual({
      ok: true,
      values: { EMAIL: 'a@example.com' },
    });
  });

  it('rejects nullable unique locator values', () => {
    const locator = resolveEditRowLocator({
      dbType: 'mysql',
      resultColumns: ['EMAIL', 'NAME'],
      indexes: [uniqueIndex('uk_email', 'EMAIL')],
    });

    expect(resolveRowLocatorValues(locator, { EMAIL: null, NAME: 'A' })).toEqual({
      ok: false,
      error: '定位列 EMAIL 的值为空，无法安全提交修改。',
    });
  });

  it('extracts DuckDB rowid locator values from the original row', () => {
    const locator = resolveEditRowLocator({
      dbType: 'duckdb',
      resultColumns: ['name', DUCKDB_ROWID_LOCATOR_COLUMN],
      allowDuckDBRowID: true,
    });

    expect(resolveRowLocatorValues(locator, { name: 'launch', [DUCKDB_ROWID_LOCATOR_COLUMN]: 17 })).toEqual({
      ok: true,
      values: { rowid: 17 },
    });
  });
});

describe('filterHiddenLocatorColumns', () => {
  it('removes hidden Oracle ROWID columns from displayed columns', () => {
    const locator = resolveEditRowLocator({
      dbType: 'oracle',
      resultColumns: ['NAME', ORACLE_ROWID_LOCATOR_COLUMN],
      allowOracleRowID: true,
    });

    expect(filterHiddenLocatorColumns(['NAME', ORACLE_ROWID_LOCATOR_COLUMN], locator)).toEqual(['NAME']);
  });

  it('removes hidden DuckDB rowid columns from displayed columns', () => {
    const locator = resolveEditRowLocator({
      dbType: 'duckdb',
      resultColumns: ['name', DUCKDB_ROWID_LOCATOR_COLUMN],
      allowDuckDBRowID: true,
    });

    expect(filterHiddenLocatorColumns(['name', DUCKDB_ROWID_LOCATOR_COLUMN], locator)).toEqual(['name']);
  });
});
