import { useEffect, useRef, useState } from 'react';
import { appendClientIdHeader } from './clientIdentity';

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

export type AutomationContextInput = {
  id?: string;
  title: string;
  content?: string;
  enabled?: boolean;
  defaultSelected?: boolean;
};

const AUTOMATION_CONTEXTS_API_PATH = '/automation-contexts';
const AUTOMATION_CONTEXT_SYNC_EVENT = 'work-app:automation-contexts-changed';
const AUTOMATION_CONTEXT_REQUEST_TIMEOUT_MS = 8000;

export const DEFAULT_AUTOMATION_CONTEXTS: AutomationContextRecord[] = [
  {
    id: 'general-inquiry-default',
    title: '기본 확인',
    content: '소스 수정 없이 조회, 상태 확인, 응답 정리를 우선합니다.',
    enabled: true,
    defaultSelected: true,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'none-default',
    title: '기본 처리',
    content:
      '## 기본 처리\n- 현재 저장소 규칙과 요청 본문을 우선 따릅니다.\n- 소스 수정이 필요하면 관련 파일을 수정하고, 필요 없으면 확인 결과만 정리합니다.\n- 작업 결과에는 변경 파일, diff, preview 같은 증적을 남길 수 있습니다.',
    enabled: true,
    defaultSelected: true,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'plan-default',
    title: '문서형 처리',
    content: 'Plan 등록/문서형 요청으로 처리하고, 구현보다 등록/정리 결과를 우선 남깁니다.',
    enabled: true,
    defaultSelected: false,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'command-execution-default',
    title: '명령 실행',
    content: '저장소 수정 없이 명령 실행, API 확인, 상태 조회 중심으로 처리합니다.',
    enabled: true,
    defaultSelected: false,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'non-source-work-default',
    title: '비소스 작업',
    content: '문서, 운영, 데이터 확인 등 비소스 작업으로 처리하고 코드 수정은 요청 시에만 제한적으로 수행합니다.',
    enabled: true,
    defaultSelected: false,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    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',
  },
];

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

function compareContextUpdatedAt(left: AutomationContextRecord, right: AutomationContextRecord) {
  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<AutomationContextRecord>): AutomationContextRecord | 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<AutomationContextRecord>[] | null | undefined) {
  const byId = new Map<string, AutomationContextRecord>();
  const bySemanticKey = new Map<string, AutomationContextRecord>();

  (items ?? [])
    .map((item) => normalizeAutomationContext(item))
    .filter((item): item is AutomationContextRecord => 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);
    }
  }

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

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

  window.dispatchEvent(new Event(AUTOMATION_CONTEXT_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_CONTEXT_REQUEST_TIMEOUT_MS);

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

  try {
    const response = await fetch(`${baseUrl}${AUTOMATION_CONTEXTS_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()) || '자동화 Context 요청에 실패했습니다.');
    }

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

async function requestAutomationContexts<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 loadAutomationContextsFromServer() {
  const response = await requestAutomationContexts<{ ok: boolean; automationContexts: Partial<AutomationContextRecord>[] | null }>({
    method: 'GET',
  });

  if (response.automationContexts == null) {
    return DEFAULT_AUTOMATION_CONTEXTS;
  }

  return sanitizeAutomationContexts(response.automationContexts);
}

async function saveAutomationContextsToServer(items: AutomationContextRecord[]) {
  const resolved = sanitizeAutomationContexts(items);
  const response = await requestAutomationContexts<{ ok: boolean; automationContexts: Partial<AutomationContextRecord>[] }>({
    method: 'PUT',
    body: JSON.stringify({ automationContexts: resolved }),
  });

  return sanitizeAutomationContexts(response.automationContexts);
}

export function upsertAutomationContext(items: AutomationContextRecord[], input: AutomationContextInput) {
  const nextItem = normalizeAutomationContext(input);

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

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

export function deleteAutomationContext(items: AutomationContextRecord[], automationContextId: string) {
  const normalizedId = normalizeText(automationContextId);

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

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

export function buildAutomationContextOptions(
  items: AutomationContextRecord[],
  selectedContextIds: string[] = [],
) {
  const contexts = sanitizeAutomationContexts(items);
  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: AutomationContextRecord[]) {
  return sanitizeAutomationContexts(items)
    .filter((item) => item.enabled && item.defaultSelected)
    .map((item) => item.id);
}

export function useAutomationContextRegistry() {
  const [automationContexts, setAutomationContextsState] = useState<AutomationContextRecord[]>(DEFAULT_AUTOMATION_CONTEXTS);
  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 nextAutomationContexts = await loadAutomationContextsFromServer();

        if (!cancelled && mountedRef.current) {
          setAutomationContextsState(nextAutomationContexts);
        }
      } catch (error) {
        if (!cancelled && mountedRef.current) {
          setAutomationContextsState(DEFAULT_AUTOMATION_CONTEXTS);
          setErrorMessage(error instanceof Error ? error.message : '자동화 Context를 불러오지 못했습니다.');
        }
      } finally {
        if (!cancelled && mountedRef.current) {
          setIsLoading(false);
        }
      }
    };

    void load();

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

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

  const setAutomationContexts = async (nextItems: AutomationContextRecord[]) => {
    const saved = await saveAutomationContextsToServer(nextItems);

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

    emitAutomationContextsChange();
    return saved;
  };

  return {
    automationContexts,
    setAutomationContexts,
    isLoading,
    errorMessage,
  };
}
