// Data-import DOMAIN — Items List parser/validator for Cloud Import. Pure. The Items List JSON
// is AUTHORITATIVE for cloud task boundaries (MAP4/C3): each top-level entry -> exactly one Task.
// Source: items-list-spec.md §2.1 (standard schema) + §2.2 (simplified auto-split) + the rules
// IL1-IL6, P1-P2; data-import-spec.md C2/C3/C5.
//
// Two per-task shapes are accepted (detected per task, IL2):
//   • standard:   { name, series: [{ items: string | string[], name? }, …] }
//   • simplified: { name, items: string[] }   (legacy flat list, split later client-side)

import type { DataType } from '../dataTypes';
import { validateBucketRelativePath } from '../bucketPath';
import { importError, type ImportError } from '../types';

export interface Series {
  items: string | string[];
  name?: string;
}

/** One Items List entry === one Task (C3/IL1). Carries either `series` (standard) or `items` (simplified). */
export interface ItemsListTask {
  name: string;
  series?: Series[];
  items?: string[];
}

export interface ParsedItemsList {
  /** Structurally-parsed tasks (only those that had a usable name). */
  tasks: ItemsListTask[];
  /** Per-entry + structural errors, located for per-row reporting (P2). */
  errors: ImportError[];
  /** True iff JSON parsed, was an array, and every entry validated cleanly. */
  ok: boolean;
}

/**
 * Parse + validate an Items List JSON string for the given target data type.
 * P1 fail-fast: a JSON syntax error yields a single ITEMS_LIST_MALFORMED error and no tasks.
 */
export function parseItemsList(json: string, dataType: DataType): ParsedItemsList {
  let raw: unknown;
  try {
    raw = JSON.parse(json);
  } catch (e) {
    const detail = e instanceof Error ? e.message : String(e);
    return { tasks: [], errors: [importError('ITEMS_LIST_MALFORMED', `JSON không hợp lệ: ${detail}`)], ok: false };
  }

  if (!Array.isArray(raw)) {
    return {
      tasks: [],
      errors: [importError('ITEMS_LIST_NOT_ARRAY', 'Items List phải là một mảng các tác vụ (task).')],
      ok: false,
    };
  }

  const errors: ImportError[] = [];
  const tasks: ItemsListTask[] = [];
  const seenNames = new Set<string>();

  raw.forEach((entry, index) => {
    const obj = (entry ?? {}) as Record<string, unknown>;
    const name = typeof obj.name === 'string' ? obj.name.trim() : '';

    // IL5 — every task needs a unique, non-empty name.
    if (name === '') {
      errors.push(importError('ENTRY_NAME_MISSING', `Tác vụ #${index + 1} thiếu "name".`));
      return; // can't locate further errors without a name
    }
    if (seenNames.has(name)) {
      errors.push(importError('ENTRY_NAME_DUPLICATE', `Tên tác vụ "${name}" bị trùng.`, { entry: name }));
      return;
    }
    seenNames.add(name);

    const hasSeries = Array.isArray(obj.series);
    const hasItems = Array.isArray(obj.items);

    if (!hasSeries && !hasItems) {
      errors.push(importError('ENTRY_EMPTY', `Tác vụ "${name}" phải có "series" hoặc "items".`, { entry: name }));
      return;
    }

    const task: ItemsListTask = { name };
    if (hasSeries) {
      task.series = obj.series as Series[];
      validateStandard(name, task.series, dataType, errors);
    } else {
      task.items = (obj.items as unknown[]).filter((x): x is string => typeof x === 'string');
      validatePaths(name, task.items, errors);
      if (task.items.length === 0) {
        errors.push(importError('ENTRY_EMPTY', `Tác vụ "${name}" có "items" rỗng.`, { entry: name }));
      }
    }
    tasks.push(task);
  });

  return { tasks, errors, ok: errors.length === 0 };
}

/** Validate a standard-form entry's series + their item paths, applying the per-type item shape. */
function validateStandard(name: string, series: Series[], dataType: DataType, errors: ImportError[]): void {
  if (series.length === 0) {
    errors.push(importError('ENTRY_EMPTY', `Tác vụ "${name}" có "series" rỗng.`, { entry: name }));
    return;
  }
  for (const s of series) {
    const items = s?.items;
    // IL3 — a NIfTI series' items MUST be a single string; an array is invalid.
    if (dataType === 'nifti' && Array.isArray(items)) {
      errors.push(
        importError('ENTRY_ITEMS_SHAPE', `Tác vụ "${name}": NIfTI yêu cầu "items" là một chuỗi, không phải mảng.`, {
          entry: name,
        }),
      );
      continue;
    }
    const paths = typeof items === 'string' ? [items] : Array.isArray(items) ? items.filter((x): x is string => typeof x === 'string') : [];
    if (paths.length === 0) {
      errors.push(importError('ENTRY_EMPTY', `Tác vụ "${name}": một "series" không có item nào.`, { entry: name }));
    }
    validatePaths(name, paths, errors);
  }
}

/** C2/IL6 — every referenced path must be bucket-relative (not a URL/absolute). */
function validatePaths(entry: string, paths: readonly string[], errors: ImportError[]): void {
  for (const p of paths) {
    const err = validateBucketRelativePath(p, { entry, file: p });
    if (err) errors.push(err);
  }
}
