import type {
  FailureSupplementFields,
  StandardizationFailureItem,
} from '@/services/standardizedService';

const FAILURE_SUPPLEMENT_DRAFT_KEY_PREFIX = 'standardization.failureSupplementDrafts';
const FAILURE_SUPPLEMENT_DRAFT_TTL_MS = 30 * 24 * 60 * 60 * 1000;

export interface FailureSupplementDraftRecord {
  failureItemId: string;
  batchId: string;
  values: FailureSupplementFields;
  savedAt: string;
  sourceUpdatedAt?: string;
}

type StoredFailureSupplementDrafts = Record<string, FailureSupplementDraftRecord>;

function getStorage() {
  if (typeof window === 'undefined') {
    return null;
  }

  const storage = window.localStorage;
  if (
    !storage
    || typeof storage.getItem !== 'function'
    || typeof storage.setItem !== 'function'
    || typeof storage.removeItem !== 'function'
  ) {
    return null;
  }

  return storage;
}

function readJson<T>(key: string): T | null {
  const storage = getStorage();
  if (!storage) {
    return null;
  }

  const rawValue = storage.getItem(key);
  if (!rawValue) {
    return null;
  }

  try {
    return JSON.parse(rawValue) as T;
  } catch {
    storage.removeItem(key);
    return null;
  }
}

function writeJson<T>(key: string, value: T) {
  const storage = getStorage();
  if (!storage) {
    return;
  }

  storage.setItem(key, JSON.stringify(value));
}

function getDraftUserId(item: StandardizationFailureItem) {
  return item.source_snapshot.created_by || 'anonymous';
}

function getDraftStorageKey(item: Pick<StandardizationFailureItem, 'batch_id' | 'source_snapshot'>) {
  const userId = item.source_snapshot.created_by || 'anonymous';
  return `${FAILURE_SUPPLEMENT_DRAFT_KEY_PREFIX}.${userId}.${item.batch_id}`;
}

function normalizeTimestamp(value: unknown): string | null {
  if (typeof value !== 'string') {
    return null;
  }

  const timestamp = Date.parse(value);
  if (!Number.isFinite(timestamp)) {
    return null;
  }

  return new Date(timestamp).toISOString();
}

function normalizeDraftValues(value: unknown): FailureSupplementFields | null {
  if (!value || typeof value !== 'object') {
    return null;
  }

  const values = value as Record<string, unknown>;
  const next: FailureSupplementFields = {};

  for (const [fieldKey, fieldValue] of Object.entries(values)) {
    if (fieldValue !== undefined && typeof fieldValue !== 'string') {
      return null;
    }
    next[fieldKey as keyof FailureSupplementFields] = fieldValue;
  }

  return next;
}

function normalizeDraftRecord(value: unknown): FailureSupplementDraftRecord | null {
  if (!value || typeof value !== 'object') {
    return null;
  }

  const record = value as Record<string, unknown>;
  const savedAt = normalizeTimestamp(record.savedAt);
  const sourceUpdatedAt = normalizeTimestamp(record.sourceUpdatedAt);
  const values = normalizeDraftValues(record.values);

  if (
    typeof record.failureItemId !== 'string'
    || typeof record.batchId !== 'string'
    || !savedAt
    || !values
  ) {
    return null;
  }

  return {
    failureItemId: record.failureItemId,
    batchId: record.batchId,
    values,
    savedAt,
    sourceUpdatedAt: sourceUpdatedAt ?? undefined,
  };
}

function hasDraftValues(values: FailureSupplementFields) {
  return Object.values(values).some((value) => Boolean(value?.trim()));
}

function readDraftsForKey(key: string, now = new Date()): StoredFailureSupplementDrafts {
  const value = readJson<unknown>(key);
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    const storage = getStorage();
    if (value !== null && storage) {
      storage.removeItem(key);
    }
    return {};
  }

  const nowTime = now.getTime();
  const next: StoredFailureSupplementDrafts = {};
  let changed = false;

  for (const [failureItemId, rawRecord] of Object.entries(value)) {
    const record = normalizeDraftRecord(rawRecord);
    const savedTime = record ? Date.parse(record.savedAt) : Number.NaN;
    const expired = Number.isFinite(savedTime) && nowTime - savedTime > FAILURE_SUPPLEMENT_DRAFT_TTL_MS;

    if (!record || record.failureItemId !== failureItemId || expired || !hasDraftValues(record.values)) {
      changed = true;
      continue;
    }

    next[failureItemId] = record;
  }

  if (changed) {
    if (Object.keys(next).length > 0) {
      writeJson(key, next);
    } else {
      getStorage()?.removeItem(key);
    }
  }

  return next;
}

function writeDraftsForKey(key: string, drafts: StoredFailureSupplementDrafts) {
  if (Object.keys(drafts).length === 0) {
    getStorage()?.removeItem(key);
    return;
  }

  writeJson(key, drafts);
}

export function hasFailureSupplementDraftValues(values: FailureSupplementFields) {
  return hasDraftValues(values);
}

export function getStoredFailureSupplementDrafts(items: StandardizationFailureItem[]) {
  const records: StoredFailureSupplementDrafts = {};

  for (const item of items) {
    const batchDrafts = readDraftsForKey(getDraftStorageKey(item));
    const record = batchDrafts[item.id];
    if (!record) {
      continue;
    }

    records[item.id] = record;
  }

  return records;
}

export function saveFailureSupplementDraft(item: StandardizationFailureItem, values: FailureSupplementFields, savedAt = new Date()) {
  const key = getDraftStorageKey(item);
  const drafts = readDraftsForKey(key, savedAt);

  if (!hasDraftValues(values)) {
    delete drafts[item.id];
    writeDraftsForKey(key, drafts);
    return null;
  }

  const record: FailureSupplementDraftRecord = {
    failureItemId: item.id,
    batchId: item.batch_id,
    values,
    savedAt: savedAt.toISOString(),
    sourceUpdatedAt: item.updated_at,
  };

  drafts[item.id] = record;
  writeDraftsForKey(key, drafts);
  return record;
}

export function clearFailureSupplementDraft(item: StandardizationFailureItem) {
  const key = getDraftStorageKey(item);
  const drafts = readDraftsForKey(key);
  delete drafts[item.id];
  writeDraftsForKey(key, drafts);
}

export function clearFailureSupplementDrafts(items: StandardizationFailureItem[]) {
  const grouped = new Map<string, StandardizationFailureItem[]>();

  for (const item of items) {
    const key = getDraftStorageKey(item);
    grouped.set(key, [...(grouped.get(key) ?? []), item]);
  }

  for (const [key, groupItems] of grouped) {
    const drafts = readDraftsForKey(key);
    for (const item of groupItems) {
      delete drafts[item.id];
    }
    writeDraftsForKey(key, drafts);
  }
}

export function getFailureSupplementDraftScope(item: StandardizationFailureItem) {
  return {
    userId: getDraftUserId(item),
    batchId: item.batch_id,
    failureItemId: item.id,
  };
}
