import { useEffect, useRef, useState } from 'react';
import type { BoardAutomationType } from '../../features/board/types';
import type { PlanAutomationType } from '../../features/planBoard/types';
import { appendClientIdHeader } from './clientIdentity';

export const AUTOMATION_BEHAVIOR_TYPES = [
  'none',
  'plan',
  'command_execution',
  'non_source_work',
  'auto_worker',
] as const;

export type AutomationBehaviorType = (typeof AUTOMATION_BEHAVIOR_TYPES)[number];

export type AutomationTypeContextRecord = {
  id: string;
  title: string;
  content: string;
  enabled: boolean;
  defaultSelected: boolean;
  updatedAt: string;
};

export type AutomationTypeRecord = {
  id: string;
  name: string;
  description: string;
  contexts: AutomationTypeContextRecord[];
  behaviorType: AutomationBehaviorType;
  enabled: boolean;
  updatedAt: string;
};

export type AutomationTypeInput = {
  id?: string;
  name: string;
  description?: string;
  contexts?: Partial<AutomationTypeContextRecord>[];
  behaviorType?: AutomationBehaviorType;
  enabled?: boolean;
};

const AUTOMATION_TYPES_API_PATH = '/automation-types';
const AUTOMATION_TYPE_SYNC_EVENT = 'work-app:automation-types-changed';
const AUTOMATION_TYPE_REQUEST_TIMEOUT_MS = 8000;

export const AUTOMATION_BEHAVIOR_LABELS: Record<AutomationBehaviorType, string> = {
  none: '기본유형',
  plan: '작업 요청 등록',
  command_execution: 'Command 실행',
  non_source_work: '비 소스작업',
  auto_worker: 'autoWorker',
};

const DEFAULT_AUTOMATION_TYPES: AutomationTypeRecord[] = [
  {
    id: 'general-inquiry',
    name: '일반 문의',
    description: '일반 문의/확인 요청으로 처리합니다.',
    contexts: [
      {
        id: 'general-inquiry-default',
        title: '기본 확인',
        content: '소스 수정 없이 조회, 상태 확인, 응답 정리를 우선합니다.',
        enabled: true,
        defaultSelected: true,
        updatedAt: '2026-04-29T00:00:00.000Z',
      },
    ],
    behaviorType: 'command_execution',
    enabled: true,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'none',
    name: '기본유형',
    description: '기본 자동화 처리용 유형입니다.',
    contexts: [
      {
        id: 'none-default',
        title: '기본 처리',
        content:
          '## 기본 처리\n- 현재 저장소 규칙과 요청 본문을 우선 따릅니다.\n- 소스 수정이 필요하면 관련 파일을 수정하고, 필요 없으면 확인 결과만 정리합니다.\n- 작업 결과에는 변경 파일, diff, preview 같은 증적을 남길 수 있습니다.',
        enabled: true,
        defaultSelected: true,
        updatedAt: '2026-04-29T00:00:00.000Z',
      },
    ],
    behaviorType: 'none',
    enabled: true,
    updatedAt: '2026-04-23T00:00:00.000Z',
  },
  {
    id: 'plan',
    name: '작업 요청 등록',
    description: 'Plan 등록/문서형 자동화 요청으로 처리합니다.',
    contexts: [
      {
        id: 'plan-default',
        title: '문서형 처리',
        content: 'Plan 등록/문서형 요청으로 처리하고, 구현보다 등록/정리 결과를 우선 남깁니다.',
        enabled: true,
        defaultSelected: true,
        updatedAt: '2026-04-29T00:00:00.000Z',
      },
    ],
    behaviorType: 'plan',
    enabled: true,
    updatedAt: '2026-04-23T00:00:00.000Z',
  },
  {
    id: 'command_execution',
    name: 'Command 실행',
    description: '저장소 수정 없이 명령 실행/조회 중심으로 처리합니다.',
    contexts: [
      {
        id: 'command-execution-default',
        title: '명령 실행',
        content: '저장소 수정 없이 명령 실행, API 확인, 상태 조회 중심으로 처리합니다.',
        enabled: true,
        defaultSelected: true,
        updatedAt: '2026-04-29T00:00:00.000Z',
      },
    ],
    behaviorType: 'command_execution',
    enabled: true,
    updatedAt: '2026-04-23T00:00:00.000Z',
  },
  {
    id: 'non_source_work',
    name: '비 소스작업',
    description: '문서/운영/확인 등 비소스 작업으로 처리합니다.',
    contexts: [
      {
        id: 'non-source-work-default',
        title: '비소스 작업',
        content: '문서, 운영, 데이터 확인 등 비소스 작업으로 처리하고 코드 수정은 요청 시에만 제한적으로 수행합니다.',
        enabled: true,
        defaultSelected: true,
        updatedAt: '2026-04-29T00:00:00.000Z',
      },
    ],
    behaviorType: 'non_source_work',
    enabled: true,
    updatedAt: '2026-04-23T00:00:00.000Z',
  },
  {
    id: 'auto_worker',
    name: 'autoWorker',
    description: '자동화 작업메모로 처리합니다.',
    contexts: [
      {
        id: 'auto-worker-default',
        title: '자동화 기본 규칙',
        content:
          '## context 사용 규칙\n- 자동화 실행기는 선택된 자동화 유형의 context만 우선 참조합니다.\n- Codex Live 문맥이나 일반 채팅 문맥은 자동화 기본 context로 섞지 않습니다.\n\n## 처리 원칙\n- 세부 절차는 현재 운영 설정을 따릅니다.',
        enabled: true,
        defaultSelected: true,
        updatedAt: '2026-04-29T00:00:00.000Z',
      },
    ],
    behaviorType: 'auto_worker',
    enabled: true,
    updatedAt: '2026-04-23T00:00:00.000Z',
  },
];

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

export function normalizeAutomationTypeId(
  value: unknown,
): PlanAutomationType | BoardAutomationType {
  const normalized = normalizeText(typeof value === 'string' ? value : '');

  if (normalized === 'stock-alert') {
    return 'general-inquiry';
  }

  if (normalized === 'plan_registration') {
    return 'plan';
  }

  if (normalized === 'general_development') {
    return 'auto_worker';
  }

  return normalized || 'none';
}

function normalizeBehaviorType(value: unknown): AutomationBehaviorType {
  const normalized = normalizeAutomationTypeId(value);
  return AUTOMATION_BEHAVIOR_TYPES.includes(normalized as AutomationBehaviorType)
    ? (normalized as AutomationBehaviorType)
    : 'none';
}

function getSemanticKey(record: Pick<AutomationTypeRecord, 'name' | 'behaviorType'>) {
  return `${record.behaviorType}:${normalizeText(record.name).replace(/\s+/g, ' ').toLocaleLowerCase('ko-KR')}`;
}

function compareUpdatedAt(left: AutomationTypeRecord, right: AutomationTypeRecord) {
  const leftTime = Date.parse(left.updatedAt);
  const rightTime = Date.parse(right.updatedAt);

  if (Number.isFinite(leftTime) && Number.isFinite(rightTime) && leftTime !== rightTime) {
    return leftTime - rightTime;
  }

  return 0;
}

function compareContextUpdatedAt(left: AutomationTypeContextRecord, right: AutomationTypeContextRecord) {
  const leftTime = Date.parse(left.updatedAt);
  const rightTime = Date.parse(right.updatedAt);

  if (Number.isFinite(leftTime) && Number.isFinite(rightTime) && leftTime !== rightTime) {
    return leftTime - rightTime;
  }

  return 0;
}

function normalizeAutomationContext(record: Partial<AutomationTypeContextRecord>): AutomationTypeContextRecord | null {
  const title = normalizeText(record.title);
  const content = normalizeText(record.content);

  if (!title && !content) {
    return null;
  }

  const id =
    normalizeText(record.id) ||
    `automation-context-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;

  return {
    id,
    title: title || 'Context',
    content,
    enabled: record.enabled !== false,
    defaultSelected: record.defaultSelected !== false,
    updatedAt: normalizeText(record.updatedAt) || new Date().toISOString(),
  };
}

export function sanitizeAutomationContexts(items: Partial<AutomationTypeContextRecord>[] | null | undefined) {
  const byId = new Map<string, AutomationTypeContextRecord>();
  const bySemanticKey = new Map<string, AutomationTypeContextRecord>();

  (items ?? [])
    .map((item) => normalizeAutomationContext(item))
    .filter((item): item is AutomationTypeContextRecord => Boolean(item))
    .forEach((item) => {
      const currentById = byId.get(item.id);
      if (!currentById || compareContextUpdatedAt(currentById, item) <= 0) {
        byId.set(item.id, item);
      }
    });

  for (const item of byId.values()) {
    const semanticKey = normalizeText(item.title).replace(/\s+/g, ' ').toLocaleLowerCase('ko-KR');
    const current = bySemanticKey.get(semanticKey);
    if (!current || compareContextUpdatedAt(current, item) <= 0) {
      bySemanticKey.set(semanticKey, item);
    }
  }

  return Array.from(bySemanticKey.values()).sort((left, right) => left.title.localeCompare(right.title, 'ko-KR'));
}

function normalizeAutomationType(
  record: Partial<Omit<AutomationTypeRecord, 'contexts'>> & { contexts?: Partial<AutomationTypeContextRecord>[] },
): AutomationTypeRecord | null {
  const name = normalizeText(record.name);

  if (!name) {
    return null;
  }

  const id =
    normalizeText(record.id) ||
    `automation-type-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;

  return {
    id,
    name,
    description: normalizeText(record.description),
    contexts: sanitizeAutomationContexts(record.contexts),
    behaviorType: normalizeBehaviorType(record.behaviorType),
    enabled: record.enabled !== false,
    updatedAt: normalizeText(record.updatedAt) || new Date().toISOString(),
  };
}

function sanitizeAutomationTypes(items: Partial<AutomationTypeRecord>[]) {
  const byId = new Map<string, AutomationTypeRecord>();
  const bySemanticKey = new Map<string, AutomationTypeRecord>();

  items
    .map((item) => normalizeAutomationType(item))
    .filter((item): item is AutomationTypeRecord => Boolean(item))
    .forEach((item) => {
      const currentById = byId.get(item.id);
      if (!currentById || compareUpdatedAt(currentById, item) <= 0) {
        byId.set(item.id, item);
      }
    });

  for (const item of byId.values()) {
    const semanticKey = getSemanticKey(item);
    const current = bySemanticKey.get(semanticKey);

    if (!current || compareUpdatedAt(current, item) <= 0) {
      bySemanticKey.set(semanticKey, item);
    }
  }

  const values = Array.from(bySemanticKey.values()).sort((left, right) => left.name.localeCompare(right.name, 'ko-KR'));
  return values.length > 0 ? values : DEFAULT_AUTOMATION_TYPES;
}

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

  window.dispatchEvent(new Event(AUTOMATION_TYPE_SYNC_EVENT));
}

function resolveApiBaseUrl() {
  if (import.meta.env.VITE_WORK_SERVER_URL) {
    return import.meta.env.VITE_WORK_SERVER_URL;
  }

  return '/api';
}

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

  const hostname = window.location.hostname;

  if (!['localhost', '127.0.0.1', '0.0.0.0'].includes(hostname)) {
    return null;
  }

  const fallbackUrl = new URL(window.location.origin);
  fallbackUrl.port = '3100';
  fallbackUrl.pathname = '/api';
  fallbackUrl.search = '';
  fallbackUrl.hash = '';

  return fallbackUrl.toString().replace(/\/+$/, '');
}

const API_BASE_URL = resolveApiBaseUrl();
const FALLBACK_BASE_URL = resolveFallbackBaseUrl();

async function requestOnce<T>(baseUrl: string, init?: RequestInit) {
  const headers = appendClientIdHeader(init?.headers);
  const hasBody = init?.body !== undefined && init?.body !== null;
  const controller = new AbortController();
  const timeoutId = window.setTimeout(() => controller.abort(), AUTOMATION_TYPE_REQUEST_TIMEOUT_MS);

  if (hasBody && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json');
  }

  try {
    const response = await fetch(`${baseUrl}${AUTOMATION_TYPES_API_PATH}`, {
      ...init,
      headers,
      signal: controller.signal,
      cache: init?.cache ?? (init?.method?.toUpperCase() === 'GET' ? 'no-store' : undefined),
    });

    if (!response.ok) {
      throw new Error(await response.text() || '자동화 처리 유형 요청에 실패했습니다.');
    }

    return response.json() as Promise<T>;
  } finally {
    window.clearTimeout(timeoutId);
  }
}

async function requestAutomationTypes<T>(init?: RequestInit) {
  try {
    return await requestOnce<T>(API_BASE_URL, init);
  } catch (error) {
    const shouldRetryWithFallback =
      FALLBACK_BASE_URL &&
      FALLBACK_BASE_URL !== API_BASE_URL &&
      error instanceof Error &&
      /404|408|502|Failed to fetch|Load failed|NetworkError|응답이 지연/i.test(error.message);

    if (!shouldRetryWithFallback) {
      throw error;
    }

    return requestOnce<T>(FALLBACK_BASE_URL, init);
  }
}

async function loadAutomationTypesFromServer() {
  const response = await requestAutomationTypes<{ ok: boolean; automationTypes: Partial<AutomationTypeRecord>[] | null }>({
    method: 'GET',
  });

  if (response.automationTypes == null) {
    return DEFAULT_AUTOMATION_TYPES;
  }

  return sanitizeAutomationTypes(response.automationTypes);
}

async function saveAutomationTypesToServer(items: AutomationTypeRecord[]) {
  const resolved = sanitizeAutomationTypes(items);
  const response = await requestAutomationTypes<{ ok: boolean; automationTypes: Partial<AutomationTypeRecord>[] }>({
    method: 'PUT',
    body: JSON.stringify({ automationTypes: resolved }),
  });

  return sanitizeAutomationTypes(response.automationTypes);
}

export function upsertAutomationType(items: AutomationTypeRecord[], input: AutomationTypeInput) {
  const nextItem = normalizeAutomationType(input);

  if (!nextItem) {
    return sanitizeAutomationTypes(items);
  }

  const nextItems = items.filter((item) => item.id !== nextItem.id);
  nextItems.push(nextItem);
  return sanitizeAutomationTypes(nextItems);
}

export function deleteAutomationType(items: AutomationTypeRecord[], automationTypeId: string) {
  const normalizedId = normalizeText(automationTypeId);

  if (!normalizedId) {
    return sanitizeAutomationTypes(items);
  }

  return sanitizeAutomationTypes(items.filter((item) => item.id !== normalizedId));
}

export function resolveAutomationTypeLabel(items: AutomationTypeRecord[], automationTypeId: string | null | undefined) {
  const normalizedId = normalizeAutomationTypeId(automationTypeId);
  return items.find((item) => item.id === normalizedId)?.name ?? normalizedId;
}

export function buildAutomationTypeOptions(
  items: AutomationTypeRecord[],
  value?: string | null,
) {
  const normalizedValue = normalizeAutomationTypeId(value);
  const enabledItems = items.filter((item) => item.enabled);
  const currentItem = items.find((item) => item.id === normalizedValue);
  const source = currentItem && !enabledItems.some((item) => item.id === currentItem.id) ? [...enabledItems, currentItem] : enabledItems;

  if (source.length === 0) {
    return [{ label: '선택 안함', value: 'none' }];
  }

  return source.map((item) => ({
    label: item.name,
    value: item.id,
  }));
}

export function buildAutomationContextOptions(
  items: AutomationTypeRecord[],
  automationTypeId: string | null | undefined,
  selectedContextIds: string[] = [],
) {
  const normalizedId = normalizeAutomationTypeId(automationTypeId);
  const automationType = items.find((item) => item.id === normalizedId) ?? null;
  const contexts = sanitizeAutomationContexts(automationType?.contexts);
  const selectedSet = new Set(selectedContextIds);
  const enabledIds = new Set(contexts.filter((item) => item.enabled).map((item) => item.id));

  return contexts
    .filter((item) => enabledIds.has(item.id) || selectedSet.has(item.id))
    .map((item) => ({
      label: item.title,
      value: item.id,
    }));
}

export function resolveDefaultAutomationContextIds(
  items: AutomationTypeRecord[],
  automationTypeId: string | null | undefined,
) {
  const normalizedId = normalizeAutomationTypeId(automationTypeId);
  const automationType = items.find((item) => item.id === normalizedId) ?? null;
  return sanitizeAutomationContexts(automationType?.contexts)
    .filter((item) => item.enabled && item.defaultSelected)
    .map((item) => item.id);
}

export function useAutomationTypeRegistry() {
  const [automationTypes, setAutomationTypesState] = useState<AutomationTypeRecord[]>(DEFAULT_AUTOMATION_TYPES);
  const [isLoading, setIsLoading] = useState(true);
  const [errorMessage, setErrorMessage] = useState('');
  const mountedRef = useRef(true);

  useEffect(() => {
    mountedRef.current = true;
    return () => {
      mountedRef.current = false;
    };
  }, []);

  useEffect(() => {
    let cancelled = false;

    const load = async () => {
      setIsLoading(true);
      setErrorMessage('');

      try {
        const nextAutomationTypes = await loadAutomationTypesFromServer();

        if (!cancelled && mountedRef.current) {
          setAutomationTypesState(nextAutomationTypes);
        }
      } catch (error) {
        if (!cancelled && mountedRef.current) {
          setAutomationTypesState(DEFAULT_AUTOMATION_TYPES);
          setErrorMessage(error instanceof Error ? error.message : '자동화 처리 유형을 불러오지 못했습니다.');
        }
      } finally {
        if (!cancelled && mountedRef.current) {
          setIsLoading(false);
        }
      }
    };

    void load();

    const handleSync = () => {
      void load();
    };

    window.addEventListener(AUTOMATION_TYPE_SYNC_EVENT, handleSync);
    return () => {
      cancelled = true;
      window.removeEventListener(AUTOMATION_TYPE_SYNC_EVENT, handleSync);
    };
  }, []);

  const setAutomationTypes = async (nextItems: AutomationTypeRecord[]) => {
    const saved = await saveAutomationTypesToServer(nextItems);

    if (mountedRef.current) {
      setAutomationTypesState(saved);
      setErrorMessage('');
    }

    emitAutomationTypesChange();
    return saved;
  };

  return {
    automationTypes,
    setAutomationTypes,
    isLoading,
    errorMessage,
  };
}
