export interface CsvJsonResult {
  output: string;
  error?: string;
  rowCount?: number;
  colCount?: number;
  headers?: string[];
  rows?: Record<string, string>[];
}

/**
 * Parse CSV text into a 2D array, handling quoted fields, commas in values, and newlines in quotes.
 */
function parseCsvRows(text: string): string[][] {
  const rows: string[][] = [];
  let row: string[] = [];
  let field = "";
  let inQuotes = false;
  let i = 0;

  while (i < text.length) {
    const ch = text[i];
    if (inQuotes) {
      if (ch === '"') {
        if (i + 1 < text.length && text[i + 1] === '"') {
          field += '"';
          i += 2;
        } else {
          inQuotes = false;
          i++;
        }
      } else {
        field += ch;
        i++;
      }
    } else {
      if (ch === '"') {
        inQuotes = true;
        i++;
      } else if (ch === ",") {
        row.push(field);
        field = "";
        i++;
      } else if (ch === "\r" && text[i + 1] === "\n") {
        row.push(field);
        field = "";
        rows.push(row);
        row = [];
        i += 2;
      } else if (ch === "\n") {
        row.push(field);
        field = "";
        rows.push(row);
        row = [];
        i++;
      } else {
        field += ch;
        i++;
      }
    }
  }

  row.push(field);
  if (row.some((f) => f !== "") || rows.length === 0) {
    rows.push(row);
  }
  if (
    rows.length > 0 &&
    rows[rows.length - 1].length === 1 &&
    rows[rows.length - 1][0] === ""
  ) {
    rows.pop();
  }

  return rows;
}

function escapeCsvField(field: string): string {
  if (field.includes(",") || field.includes('"') || field.includes("\n") || field.includes("\r")) {
    return `"${field.replace(/"/g, '""')}"`;
  }
  return field;
}

/**
 * Convert CSV text (with header row) to JSON array of objects.
 */
export function convertCsvToJson(csv: string, indentSize: number = 2): CsvJsonResult {
  const trimmed = csv.trim();
  if (!trimmed) {
    return { output: "", error: "入力が空です" };
  }

  try {
    const rawRows = parseCsvRows(trimmed);
    if (rawRows.length === 0) {
      return { output: "[]", rowCount: 0, colCount: 0, headers: [], rows: [] };
    }

    const headers = rawRows[0];
    const dataRows: Record<string, string>[] = [];

    for (let r = 1; r < rawRows.length; r++) {
      const obj: Record<string, string> = {};
      for (let c = 0; c < headers.length; c++) {
        obj[headers[c]] = rawRows[r][c] ?? "";
      }
      dataRows.push(obj);
    }

    return {
      output: JSON.stringify(dataRows, null, indentSize),
      rowCount: dataRows.length,
      colCount: headers.length,
      headers,
      rows: dataRows,
    };
  } catch (e) {
    return { output: "", error: `CSVパースエラー: ${(e as Error).message}` };
  }
}

/**
 * Convert JSON array of objects to CSV text with header row.
 */
export function convertJsonToCsv(json: string): CsvJsonResult {
  const trimmed = json.trim();
  if (!trimmed) {
    return { output: "", error: "入力が空です" };
  }

  try {
    const parsed = JSON.parse(trimmed);

    if (!Array.isArray(parsed)) {
      return { output: "", error: "JSONはオブジェクトの配列である必要があります" };
    }

    if (parsed.length === 0) {
      return { output: "", rowCount: 0, colCount: 0, headers: [], rows: [] };
    }

    // Collect all keys in order of appearance
    const keySet = new Set<string>();
    for (const item of parsed) {
      if (typeof item !== "object" || item === null || Array.isArray(item)) {
        return { output: "", error: "配列の各要素はオブジェクトである必要があります" };
      }
      for (const key of Object.keys(item)) {
        keySet.add(key);
      }
    }

    const headers = Array.from(keySet);
    const lines: string[] = [];

    // Header row
    lines.push(headers.map(escapeCsvField).join(","));

    // Data rows
    const dataRows: Record<string, string>[] = [];
    for (const item of parsed) {
      const row: string[] = [];
      const obj: Record<string, string> = {};
      for (const h of headers) {
        const val = item[h] !== undefined && item[h] !== null ? String(item[h]) : "";
        row.push(escapeCsvField(val));
        obj[h] = val;
      }
      lines.push(row.join(","));
      dataRows.push(obj);
    }

    return {
      output: lines.join("\n"),
      rowCount: parsed.length,
      colCount: headers.length,
      headers,
      rows: dataRows,
    };
  } catch (e) {
    return { output: "", error: `JSONパースエラー: ${(e as Error).message}` };
  }
}
