import type { CreateProductInput } from '@meshitrack/shared';
import { ProductCategory, ServingUnit, ProductSource } from '@meshitrack/shared';

const VALID_CATEGORIES = new Set(Object.values(ProductCategory));
const VALID_UNITS = new Set(Object.values(ServingUnit));

export interface CsvParseResult {
  items: CreateProductInput[];
  errors: { row: number; message: string }[];
}

const _EXPECTED_HEADERS = [
  'name',
  'brand',
  'barcode',
  'category',
  'servingSize',
  'servingUnit',
  'densityGPerMl',
  'calories',
  'protein',
  'carbs',
  'fat',
  'fiber',
  'sugar',
  'sodium',
  'saturatedFat',
  'cholesterol',
  'tags',
];

function parseCsvLine(line: string): string[] {
  const fields: string[] = [];
  let current = '';
  let inQuotes = false;

  for (let i = 0; i < line.length; i++) {
    const ch = line[i]!;
    if (ch === '"') {
      if (inQuotes && line[i + 1] === '"') {
        current += '"';
        i++;
      } else {
        inQuotes = !inQuotes;
      }
    } else if (ch === ',' && !inQuotes) {
      fields.push(current.trim());
      current = '';
    } else {
      current += ch;
    }
  }
  fields.push(current.trim());
  return fields;
}

export function parseCsv(content: string): CsvParseResult {
  const lines = content.split(/\r?\n/).filter((l) => l.trim().length > 0);
  if (lines.length === 0) {
    return { items: [], errors: [{ row: 0, message: 'Empty file' }] };
  }

  const headers = parseCsvLine(lines[0]!).map((h) => h.toLowerCase().trim());
  const nameIdx = headers.indexOf('name');
  if (nameIdx === -1) {
    return { items: [], errors: [{ row: 0, message: 'Missing required "name" column' }] };
  }

  const items: CreateProductInput[] = [];
  const errors: { row: number; message: string }[] = [];

  for (let i = 1; i < lines.length; i++) {
    const row = i + 1;
    const fields = parseCsvLine(lines[i]!);
    try {
      const get = (header: string) => {
        const idx = headers.indexOf(header);
        return idx >= 0 && idx < fields.length ? fields[idx]! : '';
      };

      const name = get('name');
      if (!name) {
        errors.push({ row, message: 'Missing required field: name' });
        continue;
      }

      const categoryStr = get('category');
      const category =
        categoryStr && VALID_CATEGORIES.has(categoryStr as ProductCategory)
          ? (categoryStr as ProductCategory)
          : ProductCategory.OTHER;

      const unitStr = get('servingunit');
      if (unitStr && !VALID_UNITS.has(unitStr as ServingUnit)) {
        errors.push({
          row,
          message: `Invalid servingUnit: "${unitStr}". Must be g, ml, piece, or slice`,
        });
        continue;
      }
      const servingUnit =
        unitStr && VALID_UNITS.has(unitStr as ServingUnit)
          ? (unitStr as ServingUnit)
          : ServingUnit.GRAMS;

      const servingSizeStr = get('servingsize');
      const servingSize = servingSizeStr ? parseFloat(servingSizeStr) : 100;
      if (isNaN(servingSize) || servingSize <= 0) {
        errors.push({ row, message: 'servingSize must be a positive number' });
        continue;
      }

      const calories = parseFloat(get('calories') || '0') || 0;
      const protein = parseFloat(get('protein') || '0') || 0;
      const carbs = parseFloat(get('carbs') || '0') || 0;
      const fat = parseFloat(get('fat') || '0') || 0;

      const nutrition: Record<string, number> = { calories, protein, carbs, fat };

      const fiber = get('fiber');
      if (fiber) nutrition['fiber'] = parseFloat(fiber) || 0;
      const sugar = get('sugar');
      if (sugar) nutrition['sugar'] = parseFloat(sugar) || 0;
      const sodium = get('sodium');
      if (sodium) nutrition['sodium'] = parseFloat(sodium) || 0;
      const saturatedFat = get('saturatedfat');
      if (saturatedFat) nutrition['saturatedFat'] = parseFloat(saturatedFat) || 0;
      const cholesterol = get('cholesterol');
      if (cholesterol) nutrition['cholesterol'] = parseFloat(cholesterol) || 0;

      const tagsStr = get('tags');
      const tags = tagsStr
        ? tagsStr
            .split(';')
            .map((t) => t.trim())
            .filter(Boolean)
        : [];

      const brand = get('brand') || undefined;
      const barcode = get('barcode') || undefined;
      const densityStr = get('densitygperml');
      const densityGPerMl = densityStr ? parseFloat(densityStr) : undefined;

      const item: CreateProductInput = {
        name,
        category,
        servingSize,
        servingUnit,
        nutrition: nutrition as CreateProductInput['nutrition'],
        tags,
        source: ProductSource.IMPORT,
        ...(brand ? { brand } : {}),
        ...(barcode ? { barcode } : {}),
        ...(densityGPerMl && !isNaN(densityGPerMl) ? { densityGPerMl } : {}),
      };

      items.push(item);
    } catch /* v8 ignore next */ {
      errors.push({ row, message: 'Failed to parse row' });
    }
  }

  return { items, errors };
}

export const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB
export const MAX_ROWS = 5000;
