import { SearchResult, SearchStageSummary } from '@/types';
import { getSupabaseClient } from '@/lib/supabase';

type ContactDetection = Array<{
  type?: string;
  value?: string;
  source?: string;
  context?: string;
}> | null;

type FinalResultProductRow = {
  id: string;
  task_id: string | null;
  batch_id: string;
  material_id: string;
  main_keyword: string;
  stage_order: number | null;
  stage_label: string;
  stage_kind: string;
  stage_main_keyword: string;
  stage_keyword_hit_rate: number | null;
  is_placeholder: boolean;
  placeholder_reason: string;
  rank: number;
  title: string;
  offer_url: string;
  image_url: string;
  merchant_name: string;
  merchant_url: string;
  wangwang_url: string;
  offer_screenshot_url: string;
  contact_page_url: string;
  detected_contacts: ContactDetection;
  score?: number | null;
  is_match?: boolean | null;
  reason?: string;
  has_phone_contact?: boolean;
  qualification_status?: string;
  contact_collection_reason?: string;
  created_at: string;
  finished_at: string;
};

type SearchRoundRow = {
  task_id: string | null;
  batch_id: string;
  material_id: string;
  search_round?: number | null;
  stage_order: number | null;
  stage_label?: string;
  stage_kind?: string;
  stage_main_keyword?: string;
  keyword_hit_rate: number | null;
  source_candidate_count?: number | null;
  shortage_reason?: string;
  require_phone_contact?: boolean;
  is_valid_search: boolean;
  stop_reason?: string;
  contact_enrichment_skip_reason?: string;
  created_at?: string;
};

type ProcurementMaterialLookupRow = {
  id: string;
  batch_id: string;
  material_name: string;
};

type StandardizedResultLookupRow = {
  batch_id: string;
  material_id: string;
  standardized_name: string;
  primary_search_term: string;
};

type SearchTaskItemLookupRow = {
  task_id: string;
  batch_id: string;
  material_id: string;
  created_at?: string | null;
};

function normalizeText(value: string | null | undefined) {
  return value?.trim() ?? '';
}

function normalizeTaskId(value: string | null | undefined) {
  const normalized = normalizeText(value);
  return normalized || null;
}

function resolveQualificationStatus(row: Pick<FinalResultProductRow, 'is_placeholder' | 'qualification_status' | 'has_phone_contact'>, phone: string, mobile: string) {
  if (row.is_placeholder) {
    return 'placeholder';
  }

  const explicit = normalizeText(row.qualification_status);
  if (explicit) {
    return explicit;
  }

  if (row.has_phone_contact === true || Boolean(phone || mobile)) {
    return 'qualified';
  }

  return 'rejected_no_phone';
}

function contactValueByType(contacts: ContactDetection, type: string) {
  const matched = (contacts ?? []).find((contact) => contact?.type === type && contact?.value);
  return matched?.value?.trim() ?? '';
}

function firstContactType(contacts: ContactDetection) {
  return (contacts ?? []).find((contact) => contact?.type)?.type?.trim() ?? '';
}

function buildLookupKey(batchId: string, materialId: string, stageOrder?: number | null) {
  if (stageOrder != null) {
    return `${batchId}::${materialId}::${stageOrder}`;
  }

  return `${batchId}::${materialId}`;
}

function buildTaskScopedStageLookupKey(batchId: string, materialId: string, stageOrder: number | null | undefined, taskId: string | null | undefined) {
  return `${buildLookupKey(batchId, materialId, stageOrder)}::${normalizeTaskId(taskId) || '__missing_task__'}`;
}

function buildTaskTimelineLookup(rows: SearchTaskItemLookupRow[]) {
  const lookup = new Map<string, SearchTaskItemLookupRow[]>();

  for (const row of rows) {
    const key = buildLookupKey(row.batch_id, row.material_id);
    const current = lookup.get(key) ?? [];
    current.push(row);
    lookup.set(key, current);
  }

  lookup.forEach((items, key) => {
    lookup.set(
      key,
      [...items].sort((left, right) => String(right.created_at ?? '').localeCompare(String(left.created_at ?? ''))),
    );
  });

  return lookup;
}

function resolveMissingTaskId(params: {
  taskId?: string | null;
  batchId: string;
  materialId: string;
  createdAt?: string | null;
  taskTimelineLookup: Map<string, SearchTaskItemLookupRow[]>;
}) {
  const explicitTaskId = normalizeTaskId(params.taskId);
  if (explicitTaskId) {
    return explicitTaskId;
  }

  const candidates = params.taskTimelineLookup.get(buildLookupKey(params.batchId, params.materialId)) ?? [];
  if (candidates.length === 0) {
    return null;
  }

  const createdAt = String(params.createdAt ?? '');
  if (createdAt) {
    const matched = candidates.find((candidate) => String(candidate.created_at ?? '') <= createdAt);
    if (matched?.task_id) {
      return normalizeTaskId(matched.task_id);
    }
  }

  return normalizeTaskId(candidates[0]?.task_id);
}

function resolveResultRowTaskIds(rows: FinalResultProductRow[], taskItemRows: SearchTaskItemLookupRow[]) {
  const taskTimelineLookup = buildTaskTimelineLookup(taskItemRows);

  return rows.map((row) => ({
    ...row,
    task_id: resolveMissingTaskId({
      taskId: row.task_id,
      batchId: row.batch_id,
      materialId: row.material_id,
      createdAt: row.created_at || row.finished_at,
      taskTimelineLookup,
    }),
  }));
}

function resolveStageSummaryTaskIds(rows: SearchRoundRow[], taskItemRows: SearchTaskItemLookupRow[]) {
  const taskTimelineLookup = buildTaskTimelineLookup(taskItemRows);

  return rows.map((row) => ({
    ...row,
    task_id: resolveMissingTaskId({
      taskId: row.task_id,
      batchId: row.batch_id,
      materialId: row.material_id,
      createdAt: row.created_at,
      taskTimelineLookup,
    }),
  }));
}

function buildHitRateLookup(rows: SearchRoundRow[]) {
  const lookup = new Map<string, number | null>();

  for (const row of rows) {
    const key = buildTaskScopedStageLookupKey(row.batch_id, row.material_id, row.stage_order, row.task_id);
    if (!lookup.has(key) || row.is_valid_search) {
      lookup.set(key, row.keyword_hit_rate);
    }
  }

  return lookup;
}

function mapFinalResultProducts(params: {
  resultRows: FinalResultProductRow[];
  materialRows: ProcurementMaterialLookupRow[];
  standardizedRows: StandardizedResultLookupRow[];
  hitRateRows: SearchRoundRow[];
}): SearchResult[] {
  const materialNameMap = new Map(
    params.materialRows.map((row) => [buildLookupKey(row.batch_id, row.id), normalizeText(row.material_name)]),
  );
  const standardizedNameMap = new Map(
    params.standardizedRows.map((row) => [buildLookupKey(row.batch_id, row.material_id), normalizeText(row.standardized_name)]),
  );
  const primaryKeywordMap = new Map(
    params.standardizedRows.map((row) => [buildLookupKey(row.batch_id, row.material_id), normalizeText(row.primary_search_term)]),
  );
  const hitRateMap = buildHitRateLookup(params.hitRateRows);

  return params.resultRows.map((row) => {
    const lookupKey = buildLookupKey(row.batch_id, row.material_id);
    const stageKey = buildTaskScopedStageLookupKey(row.batch_id, row.material_id, row.stage_order, row.task_id);
    const legacyStageKey = buildLookupKey(row.batch_id, row.material_id, row.stage_order);
    const materialName = materialNameMap.get(lookupKey) || '';
    const standardizedName = standardizedNameMap.get(lookupKey) || materialName;
    const phone = row.is_placeholder ? '' : contactValueByType(row.detected_contacts, 'phone');
    const mobile = row.is_placeholder
      ? ''
      : (contactValueByType(row.detected_contacts, 'mobile') || (/^1\d{10}$/.test(phone) ? phone : ''));
    const qualificationStatus = resolveQualificationStatus(row, phone, mobile);
    const stageKeyword = normalizeText(row.stage_main_keyword) || normalizeText(row.main_keyword);

    return {
      id: row.id,
      task_id: row.task_id,
      batch_id: row.batch_id,
      keyword: stageKeyword || primaryKeywordMap.get(lookupKey) || standardizedName || materialName,
      material_id: row.material_id,
      material_name: materialName,
      standardized_name: standardizedName,
      result_index: row.rank,
      title: row.title || '',
      price: '',
      offer_url: row.offer_url || '',
      image_url: row.image_url || '',
      screenshot_path: '',
      screenshot_public_url: row.offer_screenshot_url || '',
      merchant_name: row.merchant_name || '',
      merchant_url: row.merchant_url || '',
      contact_type: row.is_placeholder ? '' : firstContactType(row.detected_contacts),
      contact_url: row.wangwang_url || '',
      contact_handle: '',
      contact_site: '1688',
      contact_page_url: row.contact_page_url || '',
      contact_person: '',
      phone,
      mobile,
      address: '',
      image_error: '',
      created_at: row.created_at || row.finished_at,
      score: row.score != null ? Number(row.score) : null,
      is_match: row.is_match == null ? null : Boolean(row.is_match),
      reason: normalizeText(row.reason),
      keyword_hit_rate: row.stage_keyword_hit_rate ?? hitRateMap.get(stageKey) ?? hitRateMap.get(legacyStageKey) ?? hitRateMap.get(lookupKey) ?? null,
      stage_order: row.stage_order,
      stage_label: row.stage_label || '',
      stage_kind: row.stage_kind || '',
      stage_main_keyword: stageKeyword,
      stage_keyword_hit_rate: row.stage_keyword_hit_rate ?? hitRateMap.get(stageKey) ?? hitRateMap.get(legacyStageKey) ?? null,
      is_placeholder: row.is_placeholder,
      placeholder_reason: row.placeholder_reason || '',
      has_phone_contact: qualificationStatus === 'qualified',
      qualification_status: qualificationStatus,
      contact_collection_reason: normalizeText(row.contact_collection_reason),
    };
  });
}

function mapStageSummaries(rows: SearchRoundRow[]): SearchStageSummary[] {
  const picked = new Map<string, SearchRoundRow>();

  for (const row of rows) {
    if (row.stage_order == null) {
      continue;
    }

    const key = buildTaskScopedStageLookupKey(row.batch_id, row.material_id, row.stage_order, row.task_id);
    const existing = picked.get(key);
    if (!existing || (!existing.is_valid_search && row.is_valid_search) || String(row.created_at ?? '') > String(existing.created_at ?? '')) {
      picked.set(key, row);
    }
  }

  return [...picked.values()]
    .map((row) => ({
      id: buildLookupKey(row.batch_id, row.material_id, row.stage_order),
      task_id: row.task_id,
      batch_id: row.batch_id,
      material_id: row.material_id,
      stage_order: Number(row.stage_order ?? 0),
      stage_label: row.stage_label || `阶段 ${row.stage_order}`,
      stage_kind: row.stage_kind || 'keyword',
      stage_main_keyword: row.stage_main_keyword || '',
      keyword_hit_rate: row.keyword_hit_rate,
      source_candidate_count: row.source_candidate_count ?? null,
      shortage_reason: row.shortage_reason || '',
      require_phone_contact: row.require_phone_contact ?? true,
      stop_reason: row.stop_reason || '',
      contact_enrichment_skip_reason: row.contact_enrichment_skip_reason || '',
      created_at: row.created_at || '',
    }))
    .sort((left, right) => {
      if (left.batch_id !== right.batch_id) {
        return right.batch_id.localeCompare(left.batch_id);
      }
      if (left.material_id !== right.material_id) {
        return left.material_id.localeCompare(right.material_id);
      }
      return left.stage_order - right.stage_order;
    });
}

async function deleteRelatedSearchArtifacts(
  supabase: ReturnType<typeof getSupabaseClient>,
  ids: string[],
) {
  if (ids.length === 0) {
    return;
  }

  const [contactLogsRes, customerChatsRes] = await Promise.all([
    supabase.from('contact_logs').delete().in('search_result_id', ids),
    supabase.from('customer_service_chats').delete().in('search_result_id', ids),
  ]);

  if (contactLogsRes.error) throw contactLogsRes.error;
  if (customerChatsRes.error) throw customerChatsRes.error;
}

export async function fetchSearchResults(params?: {
  batchId?: string;
  keyword?: string;
  search?: string;
  includePlaceholders?: boolean;
  includeRejectedNoPhone?: boolean;
}): Promise<SearchResult[]> {
  const supabase = getSupabaseClient();

  let query = supabase
    .from('final_result_products')
    .select('*')
    .order('created_at', { ascending: false });

  if (params?.batchId) {
    query = query.eq('batch_id', params.batchId);
  }
  if (params?.search) {
    query = query.or(`title.ilike.%${params.search}%,merchant_name.ilike.%${params.search}%`);
  }

  const { data, error } = await query;
  if (error) {
    throw error;
  }

  const resultRows = ((data ?? []) as FinalResultProductRow[])
    .filter((row) => params?.includePlaceholders || !row.is_placeholder);

  if (resultRows.length === 0) {
    return [];
  }

  const materialIds = [...new Set(resultRows.map((row) => row.material_id).filter(Boolean))];
  const batchIds = [...new Set(resultRows.map((row) => row.batch_id).filter(Boolean))];

  const [materialRes, standardizedRes, hitRateRes, taskItemRes] = await Promise.all([
    supabase
      .from('procurement_materials')
      .select('id, batch_id, material_name')
      .in('id', materialIds),
    supabase
      .from('standardized_results')
      .select('batch_id, material_id, standardized_name, primary_search_term')
      .in('material_id', materialIds),
    supabase
      .from('search_rounds')
      .select('task_id, batch_id, material_id, search_round, stage_order, stage_label, stage_kind, stage_main_keyword, keyword_hit_rate, source_candidate_count, shortage_reason, require_phone_contact, is_valid_search, stop_reason, contact_enrichment_skip_reason, created_at')
      .in('batch_id', batchIds)
      .in('material_id', materialIds)
      .order('is_valid_search', { ascending: false }),
    supabase
      .from('search_task_items')
      .select('task_id, batch_id, material_id, created_at')
      .in('batch_id', batchIds)
      .in('material_id', materialIds)
      .order('created_at', { ascending: false }),
  ]);

  if (materialRes.error) throw materialRes.error;
  if (standardizedRes.error) throw standardizedRes.error;
  if (hitRateRes.error) throw hitRateRes.error;
  if (taskItemRes.error) throw taskItemRes.error;

  const taskItemRows = (taskItemRes.data ?? []) as SearchTaskItemLookupRow[];
  const resolvedResultRows = resolveResultRowTaskIds(resultRows, taskItemRows);
  const resolvedHitRateRows = resolveStageSummaryTaskIds((hitRateRes.data ?? []) as SearchRoundRow[], taskItemRows);

  let mapped = mapFinalResultProducts({
    resultRows: resolvedResultRows,
    materialRows: (materialRes.data ?? []) as ProcurementMaterialLookupRow[],
    standardizedRows: (standardizedRes.data ?? []) as StandardizedResultLookupRow[],
    hitRateRows: resolvedHitRateRows,
  });

  if (params?.keyword) {
    mapped = mapped.filter((row) => row.keyword === params.keyword);
  }

  if (!params?.includeRejectedNoPhone) {
    mapped = mapped.filter((row) => row.is_placeholder || row.qualification_status !== 'rejected_no_phone');
  }

  return mapped.sort((left, right) => right.created_at.localeCompare(left.created_at));
}

export async function fetchSearchStageSummaries(params?: {
  batchId?: string;
}): Promise<SearchStageSummary[]> {
  const supabase = getSupabaseClient();
  let query = supabase
    .from('search_rounds')
    .select('task_id, batch_id, material_id, search_round, stage_order, stage_label, stage_kind, stage_main_keyword, keyword_hit_rate, source_candidate_count, shortage_reason, require_phone_contact, is_valid_search, stop_reason, contact_enrichment_skip_reason, created_at')
    .not('stage_order', 'is', null)
    .order('created_at', { ascending: false });

  if (params?.batchId) {
    query = query.eq('batch_id', params.batchId);
  }

  const { data, error } = await query;
  if (error) {
    throw error;
  }

  const stageRows = (data ?? []) as SearchRoundRow[];
  if (stageRows.length === 0) {
    return [];
  }

  const batchIds = [...new Set(stageRows.map((row) => row.batch_id).filter(Boolean))];
  const materialIds = [...new Set(stageRows.map((row) => row.material_id).filter(Boolean))];
  const { data: taskItemData, error: taskItemError } = await supabase
    .from('search_task_items')
    .select('task_id, batch_id, material_id, created_at')
    .in('batch_id', batchIds)
    .in('material_id', materialIds)
    .order('created_at', { ascending: false });

  if (taskItemError) {
    throw taskItemError;
  }

  return mapStageSummaries(resolveStageSummaryTaskIds(stageRows, (taskItemData ?? []) as SearchTaskItemLookupRow[]));
}

export async function deleteSearchResultsByIds(ids: string[]): Promise<void> {
  if (ids.length === 0) return;

  const supabase = getSupabaseClient();

  await deleteRelatedSearchArtifacts(supabase, ids);

  const { error } = await supabase.from('final_result_products').delete().in('id', ids);

  if (error) throw error;
}

export async function deleteSearchStageGroup(params: {
  batchId: string;
  materialId: string;
  stageOrder: number;
}): Promise<void> {
  const supabase = getSupabaseClient();

  const { data: finalRows, error: finalRowsError } = await supabase
    .from('final_result_products')
    .select('id')
    .eq('batch_id', params.batchId)
    .eq('material_id', params.materialId)
    .eq('stage_order', params.stageOrder);

  if (finalRowsError) {
    throw finalRowsError;
  }

  const resultIds = (finalRows ?? []).map((row) => String(row.id));

  await deleteRelatedSearchArtifacts(supabase, resultIds);

  const [finalResultProductsRes, searchRoundsRes] = await Promise.all([
    supabase
      .from('final_result_products')
      .delete()
      .eq('batch_id', params.batchId)
      .eq('material_id', params.materialId)
      .eq('stage_order', params.stageOrder),
    supabase
      .from('search_rounds')
      .delete()
      .eq('batch_id', params.batchId)
      .eq('material_id', params.materialId)
      .eq('stage_order', params.stageOrder),
  ]);

  if (finalResultProductsRes.error) throw finalResultProductsRes.error;
  if (searchRoundsRes.error) throw searchRoundsRes.error;
}
