export interface ColumnSnapshot {
  sqlType: string;
  nullable: boolean;
  primaryKey: boolean;
  unique: boolean;
  default?: string;
  references?: { table: string; column: string; onDelete?: 'cascade' | 'set null' | 'restrict' };
}

export interface TableSnapshot {
  columns: Record<string, ColumnSnapshot>;
  indexes: Array<{ columns: string[]; unique: boolean }>;
  access?: { read?: string; write?: string };
}

export interface SchemaSnapshot {
  version: 1;
  tables: Record<string, TableSnapshot>;
}

const README = `# briven/_generated

Generated by briven cli. Do not edit by hand. Regenerated on every \`briven dev\`
push and at the end of \`briven\` wizard runs.
`;

function tsTypeFor(sqlType: string): string {
  if (sqlType === 'text' || sqlType === 'uuid' || sqlType === 'timestamptz') return 'string';
  if (sqlType.startsWith('varchar(')) return 'string';
  if (sqlType === 'integer') return 'number';
  if (sqlType === 'bigint') return 'bigint';
  if (sqlType === 'boolean') return 'boolean';
  if (sqlType === 'jsonb') return 'unknown';
  if (sqlType.startsWith('vector(')) return 'number[]';
  return 'unknown';
}

function pascal(s: string): string {
  return s.charAt(0).toUpperCase() + s.slice(1);
}

function renderTable(name: string, table: TableSnapshot): string {
  const lines: string[] = [];
  lines.push(`export interface ${pascal(name)} {`);
  for (const [col, def] of Object.entries(table.columns)) {
    const ts = tsTypeFor(def.sqlType);
    const tail = def.nullable ? ' | null' : '';
    lines.push(`  ${col}: ${ts}${tail};`);
  }
  lines.push('}');
  return lines.join('\n');
}

function renderDataModel(snapshot: SchemaSnapshot): string {
  const interfaces = Object.entries(snapshot.tables)
    .map(([name, table]) => renderTable(name, table))
    .join('\n\n');
  const tableMapEntries = Object.keys(snapshot.tables)
    .map((name) => `  ${name}: ${pascal(name)};`)
    .join('\n');
  const tablesType =
    Object.keys(snapshot.tables).length === 0
      ? 'export type Tables = Record<string, never>;'
      : `export interface Tables {\n${tableMapEntries}\n}`;
  return `// Generated by briven cli. Do not edit by hand.\n\n${interfaces}${interfaces ? '\n\n' : ''}${tablesType}\n\nexport type Doc<T extends keyof Tables> = Tables[T];\nexport type Id<_T extends keyof Tables> = string;\n`;
}

function renderApi(functionFilenames: string[]): string {
  const names = functionFilenames
    .map((f) => f.replace(/\.ts$/, ''))
    .filter((n) => /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(n));
  if (names.length === 0) {
    return `// Generated by briven cli. Do not edit by hand.\n\nexport const api = {} as const;\n`;
  }
  const imports = names.map((n) => `import ${n} from '../functions/${n}.js';`).join('\n');
  const entries = names.map((n) => `  ${n},`).join('\n');
  return `// Generated by briven cli. Do not edit by hand.\n\n${imports}\n\nexport const api = {\n${entries}\n} as const;\n`;
}

export function generate(
  snapshot: SchemaSnapshot,
  functionFilenames: string[],
): Map<string, string> {
  const out = new Map<string, string>();
  out.set('briven/_generated/dataModel.d.ts', renderDataModel(snapshot));
  out.set('briven/_generated/api.ts', renderApi(functionFilenames));
  out.set('briven/_generated/README.md', README);
  return out;
}
