import type { InquiryFunnelConfig, SearchPipelineConfig } from '@/types';
import { getCurrentAccessToken } from '@/services/dataAccessScope';

const DEFAULT_AUTOMATION_PORT = '3030';

export interface AutomationBaseUrlOptions {
  explicitBaseUrl?: string;
  isDev?: boolean;
  origin?: string;
  protocol?: string;
  hostname?: string;
  port?: string;
}

function trimTrailingSlash(value: string): string {
  return value.replace(/\/+$/, '');
}

export function resolveAutomationBaseUrl(options: AutomationBaseUrlOptions = {}): string {
  const explicitBaseUrl = options.explicitBaseUrl?.trim();
  if (explicitBaseUrl) {
    return trimTrailingSlash(explicitBaseUrl);
  }

  if (options.isDev) {
    return '';
  }

  if (options.origin && !options.port) {
    return trimTrailingSlash(options.origin);
  }

  if (options.protocol && options.hostname) {
    return `${options.protocol}//${options.hostname}${options.port ? `:${options.port}` : ''}`;
  }

  return `http://127.0.0.1:${options.port ?? DEFAULT_AUTOMATION_PORT}`;
}

const AUTOMATION_BASE_URL = resolveAutomationBaseUrl({
  explicitBaseUrl: import.meta.env.VITE_AUTOMATION_BASE_URL,
  isDev: import.meta.env.DEV,
  origin: typeof window !== 'undefined' ? window.location.origin : undefined,
  protocol: typeof window !== 'undefined' ? window.location.protocol : undefined,
  hostname: typeof window !== 'undefined' ? window.location.hostname : undefined,
  port: import.meta.env.VITE_AUTOMATION_PORT,
});

function buildAutomationUrl(path: string): string {
  return `${AUTOMATION_BASE_URL}${path}`;
}

export class AutomationUnauthorizedError extends Error {
  readonly status = 401;

  constructor(message: string) {
    super(message);
    this.name = 'AutomationUnauthorizedError';
  }
}

export function isAutomationUnauthorizedError(error: unknown): error is AutomationUnauthorizedError {
  return error instanceof AutomationUnauthorizedError
    || (error instanceof Error && (error as { status?: unknown }).status === 401);
}

async function readAutomationErrorMessage(res: Response, fallback: string) {
  const body = await res.json().catch(() => ({ error: fallback }));
  return typeof body?.error === 'string' && body.error.trim()
    ? body.error
    : fallback;
}

async function throwAutomationError(res: Response, fallback: string): Promise<never> {
  const message = await readAutomationErrorMessage(res, fallback);
  if (res.status === 401) {
    throw new AutomationUnauthorizedError(message);
  }

  throw new Error(message);
}

async function fetchAutomation(path: string, init: RequestInit = {}) {
  const headers = new Headers(init.headers ?? {});
  const accessToken = await getCurrentAccessToken();

  if (accessToken && !headers.has('Authorization')) {
    headers.set('Authorization', `Bearer ${accessToken}`);
  }

  return fetch(buildAutomationUrl(path), {
    ...init,
    headers,
  });
}

export type StandardizationExecutionMode = 'online' | 'offline';

export interface StandardizeProgress {
  status: 'idle' | 'pending' | 'processing' | 'stopping' | 'completed' | 'failed' | 'stopped';
  taskId: string;
  batchId: string;
  executionMode: StandardizationExecutionMode;
  providerStatus: string;
  totalRows: number;
  processedRows: number;
  progressCurrent: number;
  progressTotal: number;
  successRows: number;
  failedRows: number;
  currentItem: string;
  currentPhase: string;
  errors: string[];
  startedAt: string | null;
  finishedAt: string | null;
  result: {
    success?: boolean;
    stopped?: boolean;
    taskId?: string;
    batchId?: string;
    totalRows?: number;
    processedRows?: number;
    successRows?: number;
    failedRows?: number;
    keywordInputRowsGenerated?: number;
    searchPlanRowsGenerated?: number;
    keywordProcessed?: number;
    error?: string;
  } | null;
}

export interface StandardizeMaterialInput {
  id: string;
  material_name: string;
  specification: string;
  quantity: number;
  unit: string;
  invoice_type: string;
  brand: string;
  remarks: string;
  delivery_address: string;
}

export interface FailureSupplementRetryInput {
  failureId: string;
  supplement?: {
    spec_model?: string;
    brand?: string;
    material?: string;
    usage_scene?: string;
    execution_standard?: string;
    supplement_notes?: string;
  };
}

export interface RetryStandardizationFailuresResult {
  message: string;
  totalRows: number;
  taskId?: string;
  executionMode?: StandardizationExecutionMode;
  taskStatus?: 'pending' | 'processing' | 'completed' | 'failed';
  acceptedIds: string[];
  rejected: Array<{ failureId: string; error: string }>;
}

export async function triggerStandardizeBatch(params: {
  batchId: string;
  materialIds?: string[];
  materials?: StandardizeMaterialInput[];
}): Promise<{
  message: string;
  totalRows: number;
  taskId?: string;
  executionMode?: StandardizationExecutionMode;
  taskStatus?: 'pending' | 'processing' | 'completed' | 'failed';
}> {
  const res = await fetchAutomation('/api/standardize-batch', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      batch_id: params.batchId,
      ...(params.materialIds && params.materialIds.length > 0 ? { material_ids: params.materialIds } : {}),
      ...(params.materials && params.materials.length > 0 ? { materials: params.materials } : {}),
    }),
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: '请求失败' }));
    throw new Error(body.error ?? `HTTP ${res.status}`);
  }

  return res.json();
}

export async function retryStandardizationFailures(params: {
  failures: FailureSupplementRetryInput[];
}): Promise<RetryStandardizationFailuresResult> {
  const res = await fetchAutomation('/api/standardization-failures/retry', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      failures: params.failures.map((failure) => ({
        failure_id: failure.failureId,
        supplement: failure.supplement ?? {},
      })),
    }),
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: '请求失败' }));
    throw new Error(body.error ?? `HTTP ${res.status}`);
  }

  return res.json();
}

export async function fetchStandardizeStatus(): Promise<StandardizeProgress> {
  const res = await fetchAutomation('/api/standardize-status');

  if (!res.ok) {
    throw new Error(`状态查询失败: HTTP ${res.status}`);
  }

  return res.json();
}

export async function stopStandardizeTask(taskId?: string): Promise<{
  message: string;
  taskId?: string;
  batchId?: string;
}> {
  const res = await fetchAutomation('/api/standardize-stop', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(taskId ? { task_id: taskId } : {}),
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: '请求失败' }));
    throw new Error(body.error ?? `HTTP ${res.status}`);
  }

  return res.json();
}

// ── 1688 自动化搜索 ──────────────────────────────────────────

export interface SearchProgress {
  taskId: string;
  status: 'idle' | 'pending' | 'processing' | 'waiting_for_manual' | 'pausing' | 'paused' | 'stopping' | 'stopped' | 'completed' | 'failed';
  batchId: string;
  accountId: string;
  totalRows: number;
  processedRows: number;
  completedRows: number;
  failedRows: number;
  stoppedRows: number;
  currentRow: string;
  currentItem: string;
  displayProgressPercent?: number;
  currentStepLabel?: string;
  searchProgress?: {
    message?: string;
    materialId?: string;
    materialName?: string;
    itemProgressPercent?: number;
    softCapPercent?: number | null;
    stageStartedAt?: string;
    updatedAt?: string;
  } | null;
  errors: string[];
  result: {
    success?: boolean;
    paused?: boolean;
    stopped?: boolean;
    taskId?: string;
    batchId?: string;
    processedRows?: number;
    totalRows?: number;
    completedRows?: number;
    failedRows?: number;
    stoppedRows?: number;
    errorCount?: number;
    error?: string;
    searchShortage?: {
      targetResultCount?: number;
      qualifiedResultCount?: number;
      shortageReason?: string;
      message?: string;
    };
    searchArtifacts?: {
      runtimeRoot?: string;
      outputRoot?: string;
      preserved?: boolean;
      llmMatchResults?: string[];
      llmBatchInputs?: string[];
      llmBatchUploadedFiles?: string[];
      llmBatchRecords?: string[];
      llmBatchOutputs?: string[];
      llmBatchErrors?: string[];
      contactEnrichmentResults?: string[];
      finalSelectedProducts?: string[];
      runSummaries?: string[];
      counts?: {
        llmCandidateCount?: number;
        llmMatchedCount?: number;
        attemptedContactCount?: number;
        qualifiedResultCount?: number;
        targetResultCount?: number;
      };
    };
    manualIntervention?: {
      active?: boolean;
      alertId?: string;
      kind?: string;
      message?: string;
      taskId?: string;
      batchId?: string;
      accountId?: string;
      materialId?: string;
      materialName?: string;
      pageUrl?: string;
      detectedAt?: string;
      resolvedAt?: string | null;
      updatedAt?: string;
    };
  } | null;
  startedAt: string | null;
  pausedAt: string | null;
  finishedAt: string | null;
}

export type SearchTaskStatus = SearchProgress['status'];

export interface SearchTaskListItem {
  id: string;
  batch_id: string;
  account_id: string;
  account_name: string;
  material_id: string;
  material_name: string;
  status: SearchTaskStatus;
  total_rows: number;
  processed_rows: number;
  completed_rows: number;
  failed_rows: number;
  stopped_rows: number;
  current_material_id: string;
  current_item: string;
  current_step_label?: string;
  display_progress_percent?: number;
  search_progress?: SearchProgress['searchProgress'];
  error_message: string;
  errors: string[];
  started_at: string | null;
  paused_at: string | null;
  finished_at: string | null;
  created_at: string | null;
  updated_at: string | null;
  deletable: boolean;
}

export interface SearchTaskListSummary {
  total: number;
  pending: number;
  processing: number;
  waiting_for_manual: number;
  pausing: number;
  paused: number;
  stopping: number;
  stopped: number;
  completed: number;
  failed: number;
}

export interface SearchTaskListResponse {
  tasks: SearchTaskListItem[];
  total: number;
  summary: SearchTaskListSummary;
}

export interface SearchManualInterventionAlert {
  alertId: string;
  taskId: string;
  batchId: string;
  accountId: string;
  accountName: string;
  status: SearchTaskStatus;
  headless: boolean;
  message: string;
  kind: string;
  materialId: string;
  materialName: string;
  pageUrl: string;
  detectedAt: string;
  resolvedAt: string | null;
  updatedAt: string;
  active: boolean;
}

export type SearchExecutorStatusCode = 'idle' | 'pending' | 'processing' | 'waiting_for_manual' | 'pausing' | 'paused' | 'stopping';

export interface SearchExecutorTaskSummary {
  taskId: string;
  status: Exclude<SearchExecutorStatusCode, 'idle'>;
  batchId: string;
  accountId: string;
  totalRows: number;
  processedRows: number;
  completedRows: number;
  failedRows: number;
  stoppedRows: number;
  currentItem: string;
  startedAt: string | null;
  pausedAt: string | null;
  finishedAt: string | null;
}

export interface SearchExecutorStatus {
  state: 'idle' | 'busy';
  canStart: boolean;
  canManage: boolean;
  status: SearchExecutorStatusCode;
  message: string;
  task: SearchExecutorTaskSummary | null;
}

const SEARCH_EXECUTOR_BUSY_ERROR_CODE = 'SEARCH_EXECUTOR_BUSY';
const SEARCH_EXECUTOR_STATUS_VALUES = new Set<SearchExecutorStatusCode>(['idle', 'pending', 'processing', 'waiting_for_manual', 'pausing', 'paused', 'stopping']);

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}

function normalizeSearchExecutorTaskSummary(value: unknown): SearchExecutorTaskSummary | null {
  if (!isRecord(value)) {
    return null;
  }

  const statusCandidate = String(value.status ?? '').trim();
  if (statusCandidate === 'idle' || !SEARCH_EXECUTOR_STATUS_VALUES.has(statusCandidate as SearchExecutorStatusCode)) {
    return null;
  }

  return {
    taskId: String(value.taskId ?? ''),
    status: statusCandidate as Exclude<SearchExecutorStatusCode, 'idle'>,
    batchId: String(value.batchId ?? ''),
    accountId: String(value.accountId ?? ''),
    totalRows: Number(value.totalRows ?? 0),
    processedRows: Number(value.processedRows ?? 0),
    completedRows: Number(value.completedRows ?? 0),
    failedRows: Number(value.failedRows ?? 0),
    stoppedRows: Number(value.stoppedRows ?? 0),
    currentItem: String(value.currentItem ?? ''),
    startedAt: value.startedAt == null ? null : String(value.startedAt),
    pausedAt: value.pausedAt == null ? null : String(value.pausedAt),
    finishedAt: value.finishedAt == null ? null : String(value.finishedAt),
  };
}

function normalizeSearchExecutorStatus(value: unknown): SearchExecutorStatus {
  const record = isRecord(value) ? value : {};
  const state = record.state === 'busy' ? 'busy' : 'idle';
  const statusCandidate = String(record.status ?? (state === 'busy' ? 'processing' : 'idle')).trim();
  const normalizedStatus = SEARCH_EXECUTOR_STATUS_VALUES.has(statusCandidate as SearchExecutorStatusCode)
    ? statusCandidate as SearchExecutorStatusCode
    : state === 'busy'
      ? 'processing'
      : 'idle';
  const task = normalizeSearchExecutorTaskSummary(record.task);

  return {
    state,
    canStart: typeof record.canStart === 'boolean' ? record.canStart : state !== 'busy',
    canManage: typeof record.canManage === 'boolean' ? record.canManage : false,
    status: normalizedStatus,
    message: String(record.message ?? (state === 'busy' ? '当前搜索执行器忙碌' : '搜索执行器空闲')),
    task,
  };
}

export class SearchExecutorBusyError extends Error {
  readonly code = SEARCH_EXECUTOR_BUSY_ERROR_CODE;
  readonly executor: SearchExecutorStatus;

  constructor(message: string, executor: SearchExecutorStatus) {
    super(message);
    this.name = 'SearchExecutorBusyError';
    this.executor = executor;
  }
}

export function isSearchExecutorBusyError(error: unknown): error is SearchExecutorBusyError {
  return error instanceof SearchExecutorBusyError
    || (error instanceof Error && (error as { code?: unknown }).code === SEARCH_EXECUTOR_BUSY_ERROR_CODE);
}

export async function fetchSearchExecutorStatus(): Promise<SearchExecutorStatus> {
  const res = await fetchAutomation('/api/search-executor-status');

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: `搜索执行器状态查询失败: HTTP ${res.status}` }));
    throw new Error(body.error ?? `搜索执行器状态查询失败: HTTP ${res.status}`);
  }

  return normalizeSearchExecutorStatus(await res.json());
}

export async function triggerSearchBatch(params: {
  batch_id: string;
  material_ids?: string[];
  account_id: string;
  contact_limit?: number;
  pipeline_config?: SearchPipelineConfig;
}): Promise<{ message: string; totalRows: number; createdCount: number; taskId: string; taskIds: string[] }> {
  const res = await fetchAutomation('/api/search-batch', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params),
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: '请求失败' }));
    if (res.status === 409) {
      throw new SearchExecutorBusyError(
        body.error ?? `HTTP ${res.status}`,
        normalizeSearchExecutorStatus((body as { executor?: unknown }).executor),
      );
    }
    throw new Error(body.error ?? `HTTP ${res.status}`);
  }

  return res.json();
}

export async function fetchSearchStatus(taskId?: string): Promise<SearchProgress> {
  const url = taskId
    ? `/api/search-status?task_id=${encodeURIComponent(taskId)}`
    : '/api/search-status';
  const res = await fetchAutomation(url);

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: `搜索状态查询失败: HTTP ${res.status}` }));
    throw new Error(body.error ?? `搜索状态查询失败: HTTP ${res.status}`);
  }

  return res.json();
}

async function postSearchTaskCommand(
  path: '/api/search-pause' | '/api/search-resume' | '/api/search-stop',
  taskId?: string,
): Promise<{ message: string; taskId?: string }> {
  const res = await fetchAutomation(path, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(taskId ? { task_id: taskId } : {}),
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: '请求失败' }));
    throw new Error(body.error ?? `HTTP ${res.status}`);
  }

  return res.json();
}

export async function pauseSearchBatch(taskId?: string): Promise<{ message: string; taskId?: string }> {
  return postSearchTaskCommand('/api/search-pause', taskId);
}

export async function resumeSearchBatch(taskId?: string): Promise<{ message: string; taskId?: string }> {
  return postSearchTaskCommand('/api/search-resume', taskId);
}

export async function stopSearchBatch(taskId?: string): Promise<{ message: string; taskId?: string }> {
  return postSearchTaskCommand('/api/search-stop', taskId);
}

export async function fetchSearchTasks(params: {
  batchId?: string;
  accountId?: string;
  statuses?: SearchTaskStatus[];
  search?: string;
  limit?: number;
  offset?: number;
} = {}): Promise<SearchTaskListResponse> {
  const searchParams = new URLSearchParams();

  if (params.batchId) {
    searchParams.set('batch_id', params.batchId);
  }
  if (params.accountId) {
    searchParams.set('account_id', params.accountId);
  }
  if (params.statuses && params.statuses.length > 0) {
    searchParams.set('statuses', params.statuses.join(','));
  }
  if (params.search) {
    searchParams.set('search', params.search);
  }
  if (params.limit != null) {
    searchParams.set('limit', String(params.limit));
  }
  if (params.offset != null) {
    searchParams.set('offset', String(params.offset));
  }

  const queryString = searchParams.toString();
  const res = await fetchAutomation(`/api/search-tasks${queryString ? `?${queryString}` : ''}`);

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: `搜索任务列表查询失败: HTTP ${res.status}` }));
    throw new Error(body.error ?? `搜索任务列表查询失败: HTTP ${res.status}`);
  }

  return res.json();
}

export async function fetchSearchManualInterventions(): Promise<{ alerts: SearchManualInterventionAlert[] }> {
  const res = await fetchAutomation('/api/search-manual-interventions');

  if (!res.ok) {
    await throwAutomationError(res, `搜索人工介入提醒查询失败: HTTP ${res.status}`);
  }

  return res.json();
}

export async function openSearchManualIntervention(taskId: string): Promise<{ message: string; taskId: string; requestId: string; pageUrl: string; headless: boolean }> {
  const res = await fetchAutomation('/api/search-manual-interventions/open', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ task_id: taskId }),
  });

  if (!res.ok) {
    await throwAutomationError(res, `打开验证码会话失败: HTTP ${res.status}`);
  }

  return res.json();
}

export async function deleteSearchTask(taskId: string): Promise<{ message: string; taskId: string }> {
  const res = await fetchAutomation(`/api/search-tasks/${encodeURIComponent(taskId)}`, {
    method: 'DELETE',
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: `删除搜索任务失败: HTTP ${res.status}` }));
    throw new Error(body.error ?? `删除搜索任务失败: HTTP ${res.status}`);
  }

  return res.json();
}

// ── 智能询价 ────────────────────────────────────────────────

export interface InquiryProgress {
  status: 'idle' | 'processing' | 'completed' | 'failed' | 'stopping' | 'stopped';
  batchId: string;
  accountId: string;
  totalTasks: number;
  completedTasks: number;
  rejectedTasks: number;
  humanTakeoverTasks: number;
  currentItem: string;
  errors: string[];
  result: {
    success?: boolean;
    stopped?: boolean;
    totalTasks?: number;
    completedTasks?: number;
    rejectedTasks?: number;
    humanTakeoverTasks?: number;
    error?: string;
  } | null;
  startedAt: string | null;
  finishedAt: string | null;
}

export async function triggerInquiryBatch(params: {
  batch_id: string;
  account_id: string;
  funnel_config?: InquiryFunnelConfig;
}): Promise<{ message: string; totalTasks: number; insertedTasks: number; source: string }> {
  const res = await fetchAutomation('/api/inquiry-batch', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params),
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: '请求失败' }));
    throw new Error(body.error ?? `HTTP ${res.status}`);
  }

  return res.json();
}

export async function fetchInquiryStatus(): Promise<InquiryProgress> {
  const res = await fetchAutomation('/api/inquiry-status');

  if (!res.ok) {
    throw new Error(`询价状态查询失败: HTTP ${res.status}`);
  }

  return res.json();
}

export async function fetchInquiryStatuses(): Promise<InquiryProgress[]> {
  const res = await fetchAutomation('/api/inquiry-statuses');

  if (!res.ok) {
    throw new Error(`询价状态列表查询失败: HTTP ${res.status}`);
  }

  return res.json();
}

export async function stopInquiryBatch(batchId?: string): Promise<{ message: string; batchId?: string }> {
  const res = await fetchAutomation('/api/inquiry-stop', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(batchId ? { batch_id: batchId } : {}),
  });

  if (!res.ok) {
    const body = await res.json().catch(() => ({ error: '请求失败' }));
    throw new Error(body.error ?? `HTTP ${res.status}`);
  }

  return res.json();
}
