﻿"use client";

import { FormEvent, useCallback, useEffect, useRef, useState, useTransition } from "react";
import { Activity, Search, ShieldCheck, TrendingDown, TrendingUp } from "lucide-react";
import { useShallow } from "zustand/react/shallow";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { cn } from "@/lib/utils";
import {
  useKisRuntimeStore,
  type KisRuntimeCredentials,
} from "@/features/dashboard/store/use-kis-runtime-store";
import type {
  DashboardMarketPhase,
  DashboardKisRevokeResponse,
  DashboardPriceSource,
  DashboardKisValidateResponse,
  DashboardKisWsApprovalResponse,
  DashboardStockItem,
  DashboardStockOverviewResponse,
  DashboardStockSearchItem,
  DashboardStockSearchResponse,
  StockCandlePoint,
} from "@/features/dashboard/types/dashboard.types";

/**
 * @file features/dashboard/components/dashboard-main.tsx
 * @description 대시보드 메인 UI(검색/시세/차트)
 */

const PRICE_FORMATTER = new Intl.NumberFormat("ko-KR");

function formatPrice(value: number) {
  return `${PRICE_FORMATTER.format(value)}원`;
}

function formatVolume(value: number) {
  return `${PRICE_FORMATTER.format(value)}주`;
}

function getPriceSourceLabel(source: DashboardPriceSource, marketPhase: DashboardMarketPhase) {
  switch (source) {
    case "inquire-overtime-price":
      return "시간외 현재가(inquire-overtime-price)";
    case "inquire-ccnl":
      return marketPhase === "afterHours"
        ? "체결가 폴백(inquire-ccnl)"
        : "체결가(inquire-ccnl)";
    default:
      return "현재가(inquire-price)";
  }
}

function getMarketPhaseLabel(marketPhase: DashboardMarketPhase) {
  return marketPhase === "regular" ? "장중(한국시간 09:00~15:30)" : "장외/휴장";
}

/**
 * 주가 라인 차트(SVG)
 */
function StockLineChart({ candles }: { candles: StockCandlePoint[] }) {
  const chart = (() => {
    const width = 760;
    const height = 280;
    const paddingX = 24;
    const paddingY = 20;
    const plotWidth = width - paddingX * 2;
    const plotHeight = height - paddingY * 2;

    const prices = candles.map((item) => item.price);
    const minPrice = Math.min(...prices);
    const maxPrice = Math.max(...prices);
    const range = Math.max(maxPrice - minPrice, 1);

    const points = candles.map((item, index) => {
      const x = paddingX + (index / Math.max(candles.length - 1, 1)) * plotWidth;
      const y = paddingY + ((maxPrice - item.price) / range) * plotHeight;
      return { x, y };
    });

    const linePoints = points.map((point) => `${point.x},${point.y}`).join(" ");
    const firstPoint = points[0];
    const lastPoint = points[points.length - 1];
    const areaPoints = `${linePoints} ${lastPoint.x},${height - paddingY} ${firstPoint.x},${height - paddingY}`;

    return { width, height, paddingX, paddingY, minPrice, maxPrice, linePoints, areaPoints };
  })();

  return (
    <div className="h-[300px] w-full">
      <svg viewBox={`0 0 ${chart.width} ${chart.height}`} className="h-full w-full">
        <defs>
          <linearGradient id="priceAreaGradient" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="var(--color-brand-500)" stopOpacity="0.35" />
            <stop offset="100%" stopColor="var(--color-brand-500)" stopOpacity="0.02" />
          </linearGradient>
        </defs>

        <line
          x1={chart.paddingX}
          y1={chart.paddingY}
          x2={chart.width - chart.paddingX}
          y2={chart.paddingY}
          stroke="currentColor"
          className="text-border"
        />
        <line
          x1={chart.paddingX}
          y1={chart.height / 2}
          x2={chart.width - chart.paddingX}
          y2={chart.height / 2}
          stroke="currentColor"
          className="text-border/70"
        />
        <line
          x1={chart.paddingX}
          y1={chart.height - chart.paddingY}
          x2={chart.width - chart.paddingX}
          y2={chart.height - chart.paddingY}
          stroke="currentColor"
          className="text-border"
        />

        <polygon points={chart.areaPoints} fill="url(#priceAreaGradient)" />
        <polyline
          points={chart.linePoints}
          fill="none"
          stroke="var(--color-brand-600)"
          strokeWidth={3}
          strokeLinecap="round"
          strokeLinejoin="round"
        />
      </svg>

      <div className="mt-3 flex items-center justify-between text-xs text-muted-foreground">
        <span>{candles[0]?.time}</span>
        <span>저가 {formatPrice(chart.minPrice)}</span>
        <span>고가 {formatPrice(chart.maxPrice)}</span>
        <span>{candles[candles.length - 1]?.time}</span>
      </div>
    </div>
  );
}

function PriceStat({ label, value }: { label: string; value: string }) {
  return (
    <div className="rounded-xl border border-border/70 bg-background/70 p-3">
      <p className="text-xs text-muted-foreground">{label}</p>
      <p className="mt-1 text-sm font-semibold text-foreground">{value}</p>
    </div>
  );
}

async function fetchStockSearch(keyword: string) {
  const response = await fetch(`/api/kis/domestic/search?q=${encodeURIComponent(keyword)}`, {
    cache: "no-store",
  });

  const payload = (await response.json()) as DashboardStockSearchResponse | { error?: string };

  if (!response.ok) {
    throw new Error("error" in payload ? payload.error : "종목 검색 중 오류가 발생했습니다.");
  }

  return payload as DashboardStockSearchResponse;
}

async function fetchStockOverview(symbol: string, credentials: KisRuntimeCredentials) {
  const response = await fetch(`/api/kis/domestic/overview?symbol=${encodeURIComponent(symbol)}`, {
    method: "GET",
    headers: {
      "x-kis-app-key": credentials.appKey,
      "x-kis-app-secret": credentials.appSecret,
      "x-kis-trading-env": credentials.tradingEnv,
    },
    cache: "no-store",
  });

  const payload = (await response.json()) as DashboardStockOverviewResponse | { error?: string };

  if (!response.ok) {
    throw new Error("error" in payload ? payload.error : "종목 조회 중 오류가 발생했습니다.");
  }

  return payload as DashboardStockOverviewResponse;
}

async function validateKisCredentials(credentials: KisRuntimeCredentials) {
  const response = await fetch("/api/kis/validate", {
    method: "POST",
    headers: {
      "content-type": "application/json",
    },
    body: JSON.stringify(credentials),
    cache: "no-store",
  });

  const payload = (await response.json()) as DashboardKisValidateResponse;

  if (!response.ok || !payload.ok) {
    throw new Error(payload.message || "API 키 검증에 실패했습니다.");
  }

  return payload;
}

/**
 * KIS 접근토큰 폐기 요청
 * @param credentials 검증 완료된 KIS 키
 * @returns 폐기 응답
 * @see app/api/kis/revoke/route.ts POST - revokeP 폐기 프록시
 */
async function revokeKisCredentials(credentials: KisRuntimeCredentials) {
  const response = await fetch("/api/kis/revoke", {
    method: "POST",
    headers: {
      "content-type": "application/json",
    },
    body: JSON.stringify(credentials),
    cache: "no-store",
  });

  const payload = (await response.json()) as DashboardKisRevokeResponse;

  if (!response.ok || !payload.ok) {
    throw new Error(payload.message || "API 키 접근 폐기에 실패했습니다.");
  }

  return payload;
}

const KIS_REALTIME_TR_ID_REAL = "H0UNCNT0";
const KIS_REALTIME_TR_ID_MOCK = "H0STCNT0";

function resolveRealtimeTrId(tradingEnv: KisRuntimeCredentials["tradingEnv"]) {
  return tradingEnv === "mock" ? KIS_REALTIME_TR_ID_MOCK : KIS_REALTIME_TR_ID_REAL;
}

/**
 * KIS 실시간 웹소켓 승인키를 발급받습니다.
 * @param credentials 검증 완료된 KIS 키
 * @returns approval key + ws url
 * @see app/api/kis/ws/approval/route.ts POST - Approval 발급 프록시
 */
async function fetchKisWebSocketApproval(credentials: KisRuntimeCredentials) {
  const response = await fetch("/api/kis/ws/approval", {
    method: "POST",
    headers: {
      "content-type": "application/json",
    },
    body: JSON.stringify(credentials),
    cache: "no-store",
  });

  const payload = (await response.json()) as DashboardKisWsApprovalResponse;
  if (!response.ok || !payload.ok || !payload.approvalKey || !payload.wsUrl) {
    throw new Error(payload.message || "KIS 실시간 웹소켓 승인키 발급에 실패했습니다.");
  }

  return payload;
}

/**
 * KIS 실시간 체결가 구독/해제 메시지를 생성합니다.
 * @param approvalKey websocket 승인키
 * @param symbol 종목코드
 * @param trType "1"(구독) | "2"(해제)
 * @returns websocket 요청 메시지
 * @see https://github.com/koreainvestment/open-trading-api
 */
function buildKisRealtimeMessage(
  approvalKey: string,
  symbol: string,
  trId: string,
  trType: "1" | "2",
) {
  return {
    header: {
      approval_key: approvalKey,
      custtype: "P",
      tr_type: trType,
      "content-type": "utf-8",
    },
    body: {
      input: {
        tr_id: trId,
        tr_key: symbol,
      },
    },
  };
}

interface KisRealtimeTick {
  point: StockCandlePoint;
  price: number;
  accumulatedVolume: number;
  tickTime: string;
}

/**
 * KIS 실시간 체결가 원문을 차트 포인트로 변환합니다.
 * @param raw websocket 수신 원문
 * @param expectedSymbol 현재 선택 종목코드
 * @returns 실시간 포인트 또는 null
 */
function parseKisRealtimeTick(raw: string, expectedSymbol: string, expectedTrId: string): KisRealtimeTick | null {
  if (!/^([01])\|/.test(raw)) return null;

  const parts = raw.split("|");
  if (parts.length < 4) return null;
  if (parts[1] !== expectedTrId) return null;

  const tickCount = Number(parts[2] ?? "1");
  const values = parts[3].split("^");
  const isBatch = Number.isInteger(tickCount) && tickCount > 1 && values.length % tickCount === 0;
  const fieldsPerTick = isBatch ? values.length / tickCount : values.length;
  const baseIndex = isBatch ? (tickCount - 1) * fieldsPerTick : 0;
  const symbol = values[baseIndex];
  const hhmmss = values[baseIndex + 1];
  const price = Number((values[baseIndex + 2] ?? "").replaceAll(",", "").trim());
  const accumulatedVolume = Number((values[baseIndex + 13] ?? "").replaceAll(",", "").trim());

  if (symbol !== expectedSymbol) return null;
  if (!Number.isFinite(price) || price <= 0) return null;

  return {
    point: {
      time: formatRealtimeTickTime(hhmmss),
      price,
    },
    price,
    accumulatedVolume: Number.isFinite(accumulatedVolume) && accumulatedVolume > 0 ? accumulatedVolume : 0,
    tickTime: hhmmss ?? "",
  };
}

function formatRealtimeTickTime(hhmmss?: string) {
  if (!hhmmss || hhmmss.length !== 6) return "실시간";
  return `${hhmmss.slice(0, 2)}:${hhmmss.slice(2, 4)}:${hhmmss.slice(4, 6)}`;
}

function appendRealtimeTick(prev: StockCandlePoint[], next: StockCandlePoint) {
  if (prev.length === 0) return [next];

  const last = prev[prev.length - 1];
  if (last.time === next.time) {
    return [...prev.slice(0, -1), next];
  }

  return [...prev, next].slice(-80);
}

function toTickOrderValue(hhmmss?: string) {
  if (!hhmmss || !/^\d{6}$/.test(hhmmss)) return -1;
  return Number(hhmmss);
}

/**
 * 대시보드 메인 화면
 */
export function DashboardMain() {
  // [State] KIS 키 입력/검증 상태(zustand + persist)
  const {
    kisTradingEnvInput,
    kisAppKeyInput,
    kisAppSecretInput,
    verifiedCredentials,
    isKisVerified,
    tradingEnv,
    setKisTradingEnvInput,
    setKisAppKeyInput,
    setKisAppSecretInput,
    setVerifiedKisSession,
    invalidateKisVerification,
    clearKisRuntimeSession,
  } = useKisRuntimeStore(
    useShallow((state) => ({
      kisTradingEnvInput: state.kisTradingEnvInput,
      kisAppKeyInput: state.kisAppKeyInput,
      kisAppSecretInput: state.kisAppSecretInput,
      verifiedCredentials: state.verifiedCredentials,
      isKisVerified: state.isKisVerified,
      tradingEnv: state.tradingEnv,
      setKisTradingEnvInput: state.setKisTradingEnvInput,
      setKisAppKeyInput: state.setKisAppKeyInput,
      setKisAppSecretInput: state.setKisAppSecretInput,
      setVerifiedKisSession: state.setVerifiedKisSession,
      invalidateKisVerification: state.invalidateKisVerification,
      clearKisRuntimeSession: state.clearKisRuntimeSession,
    })),
  );

  // [State] 검증 상태 메시지
  const [kisStatusMessage, setKisStatusMessage] = useState<string | null>(null);
  const [kisStatusError, setKisStatusError] = useState<string | null>(null);

  // [State] 검색/선택 데이터
  const [keyword, setKeyword] = useState("삼성전자");
  const [searchResults, setSearchResults] = useState<DashboardStockSearchItem[]>([]);
  const [selectedStock, setSelectedStock] = useState<DashboardStockItem | null>(null);
  const [selectedOverviewMeta, setSelectedOverviewMeta] = useState<{
    priceSource: DashboardPriceSource;
    marketPhase: DashboardMarketPhase;
    fetchedAt: string;
  } | null>(null);
  const [realtimeCandles, setRealtimeCandles] = useState<StockCandlePoint[]>([]);
  const [isRealtimeConnected, setIsRealtimeConnected] = useState(false);
  const [realtimeError, setRealtimeError] = useState<string | null>(null);
  const [lastRealtimeTickAt, setLastRealtimeTickAt] = useState<number | null>(null);
  const [realtimeTickCount, setRealtimeTickCount] = useState(0);

  // [State] 영역별 에러
  const [searchError, setSearchError] = useState<string | null>(null);
  const [overviewError, setOverviewError] = useState<string | null>(null);

  // [State] 비동기 전환 상태
  const [isValidatingKis, startValidateTransition] = useTransition();
  const [isRevokingKis, startRevokeTransition] = useTransition();
  const [isSearching, startSearchTransition] = useTransition();
  const [isLoadingOverview, startOverviewTransition] = useTransition();

  const realtimeSocketRef = useRef<WebSocket | null>(null);
  const realtimeApprovalKeyRef = useRef<string | null>(null);
  const lastRealtimeTickOrderRef = useRef<number>(-1);
  const isPositive = (selectedStock?.change ?? 0) >= 0;
  const chartCandles =
    isRealtimeConnected && realtimeCandles.length > 0 ? realtimeCandles : (selectedStock?.candles ?? []);
  const apiPriceSourceLabel = selectedOverviewMeta
    ? getPriceSourceLabel(selectedOverviewMeta.priceSource, selectedOverviewMeta.marketPhase)
    : null;
  const realtimeTrId = verifiedCredentials ? resolveRealtimeTrId(verifiedCredentials.tradingEnv) : null;
  const effectivePriceSourceLabel =
    isRealtimeConnected && lastRealtimeTickAt
      ? `실시간 체결(WebSocket ${realtimeTrId ?? KIS_REALTIME_TR_ID_REAL})`
      : apiPriceSourceLabel;

  useEffect(() => {
    setRealtimeCandles([]);
    setIsRealtimeConnected(false);
    setRealtimeError(null);
    setLastRealtimeTickAt(null);
    setRealtimeTickCount(0);
    lastRealtimeTickOrderRef.current = -1;
  }, [selectedStock?.symbol]);

  useEffect(() => {
    if (!isRealtimeConnected || lastRealtimeTickAt) return;

    const noTickTimer = window.setTimeout(() => {
      setRealtimeError("실시간 연결은 되었지만 체결 데이터가 없습니다. 장중(09:00~15:30)인지 확인해 주세요.");
    }, 8000);

    return () => {
      window.clearTimeout(noTickTimer);
    };
  }, [isRealtimeConnected, lastRealtimeTickAt]);

  useEffect(() => {
    const symbol = selectedStock?.symbol;

    if (!symbol || !isKisVerified || !verifiedCredentials) {
      setIsRealtimeConnected(false);
      setRealtimeError(null);
      setRealtimeTickCount(0);
      lastRealtimeTickOrderRef.current = -1;
      realtimeSocketRef.current?.close();
      realtimeSocketRef.current = null;
      realtimeApprovalKeyRef.current = null;
      return;
    }

    let disposed = false;
    let socket: WebSocket | null = null;

    const realtimeTrId = resolveRealtimeTrId(verifiedCredentials.tradingEnv);

    const connectKisRealtimePrice = async () => {
      try {
        setRealtimeError(null);
        setIsRealtimeConnected(false);

        const approval = await fetchKisWebSocketApproval(verifiedCredentials);
        if (disposed) return;

        realtimeApprovalKeyRef.current = approval.approvalKey ?? null;
        socket = new WebSocket(`${approval.wsUrl}/tryitout`);
        realtimeSocketRef.current = socket;

        socket.onopen = () => {
          if (disposed || !realtimeApprovalKeyRef.current) return;

          const subscribeMessage = buildKisRealtimeMessage(realtimeApprovalKeyRef.current, symbol, realtimeTrId, "1");
          socket?.send(JSON.stringify(subscribeMessage));
          setIsRealtimeConnected(true);
        };

        socket.onmessage = (event) => {
          if (disposed || typeof event.data !== "string") return;

          const tick = parseKisRealtimeTick(event.data, symbol, realtimeTrId);
          if (!tick) return;

          // 지연 도착으로 시간이 역행하는 틱은 무시해 차트 흔들림을 줄입니다.
          const nextTickOrder = toTickOrderValue(tick.tickTime);
          if (nextTickOrder > 0 && lastRealtimeTickOrderRef.current > nextTickOrder) {
            return;
          }
          if (nextTickOrder > 0) {
            lastRealtimeTickOrderRef.current = nextTickOrder;
          }

          setRealtimeError(null);
          setLastRealtimeTickAt(Date.now());
          setRealtimeTickCount((prev) => prev + 1);
          setRealtimeCandles((prev) => appendRealtimeTick(prev, tick.point));

          // 실시간 체결가를 카드 현재가/등락/거래량에도 반영합니다.
          setSelectedStock((prev) => {
            if (!prev || prev.symbol !== symbol) return prev;

            const nextPrice = tick.price;
            const nextChange = nextPrice - prev.prevClose;
            const nextChangeRate = prev.prevClose > 0 ? (nextChange / prev.prevClose) * 100 : prev.changeRate;
            const nextHigh = prev.high > 0 ? Math.max(prev.high, nextPrice) : nextPrice;
            const nextLow = prev.low > 0 ? Math.min(prev.low, nextPrice) : nextPrice;

            return {
              ...prev,
              currentPrice: nextPrice,
              change: nextChange,
              changeRate: nextChangeRate,
              high: nextHigh,
              low: nextLow,
              volume: tick.accumulatedVolume > 0 ? tick.accumulatedVolume : prev.volume,
            };
          });
        };

        socket.onerror = () => {
          if (disposed) return;
          setIsRealtimeConnected(false);
          setRealtimeError("실시간 연결 중 오류가 발생했습니다.");
        };

        socket.onclose = () => {
          if (disposed) return;
          setIsRealtimeConnected(false);
        };
      } catch (error) {
        if (disposed) return;
        const message =
          error instanceof Error ? error.message : "실시간 웹소켓 초기화 중 오류가 발생했습니다.";
        setRealtimeError(message);
        setIsRealtimeConnected(false);
      }
    };

    void connectKisRealtimePrice();

    return () => {
      disposed = true;
      setIsRealtimeConnected(false);

      const approvalKey = realtimeApprovalKeyRef.current;
      if (socket?.readyState === WebSocket.OPEN && approvalKey) {
        const unsubscribeMessage = buildKisRealtimeMessage(approvalKey, symbol, realtimeTrId, "2");
        socket.send(JSON.stringify(unsubscribeMessage));
      }

      socket?.close();
      if (realtimeSocketRef.current === socket) {
        realtimeSocketRef.current = null;
      }
      realtimeApprovalKeyRef.current = null;
    };
  }, [
    isKisVerified,
    selectedStock?.symbol,
    verifiedCredentials,
  ]);

  const loadOverview = useCallback(
    async (symbol: string, credentials: KisRuntimeCredentials) => {
      try {
        setOverviewError(null);

        const data = await fetchStockOverview(symbol, credentials);
        setSelectedStock(data.stock);
        setSelectedOverviewMeta({
          priceSource: data.priceSource,
          marketPhase: data.marketPhase,
          fetchedAt: data.fetchedAt,
        });
      } catch (error) {
        const message = error instanceof Error ? error.message : "종목 조회 중 오류가 발생했습니다.";
        setOverviewError(message);
        setSelectedOverviewMeta(null);
      }
    },
    [],
  );

  const loadSearch = useCallback(
    async (nextKeyword: string, credentials: KisRuntimeCredentials, pickFirst = false) => {
      try {
        setSearchError(null);

        const data = await fetchStockSearch(nextKeyword);
        setSearchResults(data.items);

        if (pickFirst && data.items[0]) {
          await loadOverview(data.items[0].symbol, credentials);
        }
      } catch (error) {
        const message = error instanceof Error ? error.message : "종목 검색 중 오류가 발생했습니다.";
        setSearchError(message);
      }
    },
    [loadOverview],
  );

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();

    if (!isKisVerified || !verifiedCredentials) {
      setSearchError("상단에서 API 키 검증을 먼저 완료해 주세요.");
      return;
    }

    startSearchTransition(() => {
      void loadSearch(keyword, verifiedCredentials, true);
    });
  }

  function handlePickStock(item: DashboardStockSearchItem) {
    if (!isKisVerified || !verifiedCredentials) {
      setSearchError("상단에서 API 키 검증을 먼저 완료해 주세요.");
      return;
    }

    setKeyword(item.name);

    startOverviewTransition(() => {
      void loadOverview(item.symbol, verifiedCredentials);
    });
  }

  function handleValidateKis() {
    startValidateTransition(() => {
      void (async () => {
        try {
          setKisStatusError(null);
          setKisStatusMessage(null);

          const trimmedAppKey = kisAppKeyInput.trim();
          const trimmedAppSecret = kisAppSecretInput.trim();

          if (!trimmedAppKey || !trimmedAppSecret) {
            throw new Error("앱 키와 앱 시크릿을 모두 입력해 주세요.");
          }

          const credentials: KisRuntimeCredentials = {
            appKey: trimmedAppKey,
            appSecret: trimmedAppSecret,
            tradingEnv: kisTradingEnvInput,
          };

          const result = await validateKisCredentials(credentials);

          setVerifiedKisSession(credentials, result.tradingEnv);
          setKisStatusMessage(
            `${result.message} (${result.tradingEnv === "real" ? "실전" : "모의"} 모드)`,
          );

          startSearchTransition(() => {
            void loadSearch(keyword || "삼성전자", credentials, true);
          });
        } catch (error) {
          const message = error instanceof Error ? error.message : "API 키 검증 중 오류가 발생했습니다.";

          invalidateKisVerification();
          setSearchResults([]);
          setSelectedStock(null);
          setSelectedOverviewMeta(null);
          setKisStatusError(message);
        }
      })();
    });
  }

  function handleRevokeKis() {
    if (!verifiedCredentials) {
      setKisStatusError("먼저 API 키 검증을 완료해 주세요.");
      return;
    }

    startRevokeTransition(() => {
      void (async () => {
        try {
          // 접근 폐기 전, 화면 상태 메시지를 초기화합니다.
          setKisStatusError(null);
          setKisStatusMessage(null);

          const result = await revokeKisCredentials(verifiedCredentials);

          // 로그아웃처럼 검증/조회 상태를 초기화합니다.
          clearKisRuntimeSession(result.tradingEnv);
          setSearchResults([]);
          setSelectedStock(null);
          setSelectedOverviewMeta(null);
          setSearchError(null);
          setOverviewError(null);
          setKisStatusMessage(`${result.message} (${result.tradingEnv === "real" ? "실전" : "모의"} 모드)`);
        } catch (error) {
          const message = error instanceof Error ? error.message : "API 키 접근 폐기 중 오류가 발생했습니다.";
          setKisStatusError(message);
        }
      })();
    });
  }

  return (
    <div className="flex flex-col gap-6">
      {/* ========== KIS KEY VERIFY SECTION ========== */}
      <section>
        <Card className="border-brand-200 bg-gradient-to-r from-brand-50/60 to-background">
          <CardHeader>
            <CardTitle>KIS API 키 연결</CardTitle>
            <CardDescription>
              대시보드 사용 전, 개인 API 키를 입력하고 검증해 주세요. 검증에 성공해야 시세 조회가 동작합니다.
            </CardDescription>
          </CardHeader>
          <CardContent className="space-y-4">
            <div className="grid gap-3 md:grid-cols-3">
              <div className="md:col-span-1">
                <label className="mb-1 block text-xs text-muted-foreground">거래 모드</label>
                <div className="flex gap-2">
                  <Button
                    type="button"
                    variant={kisTradingEnvInput === "real" ? "default" : "outline"}
                    className={cn("flex-1", kisTradingEnvInput === "real" ? "bg-brand-600 hover:bg-brand-700" : "")}
                    onClick={() => setKisTradingEnvInput("real")}
                  >
                    실전
                  </Button>
                  <Button
                    type="button"
                    variant={kisTradingEnvInput === "mock" ? "default" : "outline"}
                    className={cn("flex-1", kisTradingEnvInput === "mock" ? "bg-brand-600 hover:bg-brand-700" : "")}
                    onClick={() => setKisTradingEnvInput("mock")}
                  >
                    모의
                  </Button>
                </div>
              </div>

              <div className="md:col-span-1">
                <label className="mb-1 block text-xs text-muted-foreground">KIS App Key</label>
                <Input
                  type="password"
                  value={kisAppKeyInput}
                  onChange={(event) => setKisAppKeyInput(event.target.value)}
                  placeholder="앱 키 입력"
                  autoComplete="off"
                />
              </div>

              <div className="md:col-span-1">
                <label className="mb-1 block text-xs text-muted-foreground">KIS App Secret</label>
                <Input
                  type="password"
                  value={kisAppSecretInput}
                  onChange={(event) => setKisAppSecretInput(event.target.value)}
                  placeholder="앱 시크릿 입력"
                  autoComplete="off"
                />
              </div>
            </div>

            <div className="flex flex-wrap items-center gap-3">
              <Button
                type="button"
                onClick={handleValidateKis}
                disabled={isValidatingKis || !kisAppKeyInput.trim() || !kisAppSecretInput.trim()}
                className="bg-brand-600 hover:bg-brand-700"
              >
                {isValidatingKis ? "검증 중..." : "API 키 검증"}
              </Button>
              <Button
                type="button"
                variant="outline"
                onClick={handleRevokeKis}
                disabled={isRevokingKis || !isKisVerified || !verifiedCredentials}
                className="border-brand-200 text-brand-700 hover:bg-brand-50 hover:text-brand-800"
              >
                {isRevokingKis ? "폐기 중..." : "접근 폐기"}
              </Button>

              {isKisVerified ? (
                <span className="rounded-full bg-brand-100 px-3 py-1 text-xs font-semibold text-brand-700">
                  검증 완료 ({tradingEnv === "real" ? "실전" : "모의"})
                </span>
              ) : (
                <span className="rounded-full bg-muted px-3 py-1 text-xs text-muted-foreground">미검증</span>
              )}
            </div>

            {kisStatusError ? <p className="text-sm text-red-600">{kisStatusError}</p> : null}
            {kisStatusMessage ? <p className="text-sm text-brand-700">{kisStatusMessage}</p> : null}

            <div className="rounded-lg border border-brand-200 bg-brand-50/70 px-3 py-2 text-xs text-brand-800">
              입력한 API 키는 새로고침 유지를 위해 현재 브라우저 저장소(zustand persist)에만 보관되며, 접근 폐기를 누르면 즉시 초기화됩니다.
            </div>
          </CardContent>
        </Card>
      </section>

      {/* ========== DASHBOARD TITLE SECTION ========== */}
      <section>
        <h2 className="text-3xl font-bold tracking-tight">국내주식 대시보드</h2>
        <p className="mt-2 text-sm text-muted-foreground">
          종목명 검색, 현재가, 일자별 차트를 한 화면에서 확인합니다.
        </p>
      </section>

      {/* ========== STOCK SEARCH SECTION ========== */}
      <section>
        <Card>
          <CardHeader>
            <CardTitle>종목 검색</CardTitle>
            <CardDescription>종목명 또는 종목코드(예: 삼성전자, 005930)로 검색할 수 있습니다.</CardDescription>
          </CardHeader>
          <CardContent className="space-y-4">
            <form onSubmit={handleSubmit} className="flex flex-col gap-3 md:flex-row">
              <div className="relative flex-1">
                <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
                <Input
                  value={keyword}
                  onChange={(event) => setKeyword(event.target.value)}
                  placeholder="종목명 / 종목코드 검색"
                  className="pl-9"
                  disabled={!isKisVerified}
                />
              </div>
              <Button type="submit" className="md:min-w-28" disabled={!isKisVerified || isSearching || !keyword.trim()}>
                {isSearching ? "검색 중..." : "검색"}
              </Button>
            </form>

            {!isKisVerified ? (
              <p className="text-xs text-muted-foreground">상단에서 API 키 검증을 완료해야 검색/시세 기능이 동작합니다.</p>
            ) : searchError ? (
              <p className="text-sm text-red-600">{searchError}</p>
            ) : (
              <p className="text-xs text-muted-foreground">
                {searchResults.length > 0
                  ? `검색 결과 ${searchResults.length}개`
                  : "검색어를 입력하고 엔터를 누르면 종목이 표시됩니다."}
              </p>
            )}

            <div className="grid gap-2 md:grid-cols-2 xl:grid-cols-5">
              {searchResults.map((item) => {
                const active = item.symbol === selectedStock?.symbol;

                return (
                  <button
                    key={`${item.symbol}-${item.market}`}
                    type="button"
                    onClick={() => handlePickStock(item)}
                    className={cn(
                      "rounded-lg border px-3 py-2 text-left transition-colors",
                      active
                        ? "border-brand-500 bg-brand-50 text-brand-700 dark:bg-brand-900/30 dark:text-brand-300"
                        : "border-border bg-background hover:bg-muted/60",
                    )}
                    disabled={!isKisVerified}
                  >
                    <p className="text-sm font-semibold">{item.name}</p>
                    <p className="text-xs text-muted-foreground">
                      {item.symbol} · {item.market}
                    </p>
                  </button>
                );
              })}
            </div>
          </CardContent>
        </Card>
      </section>

      {/* ========== STOCK OVERVIEW SECTION ========== */}
      <section className="grid gap-4 xl:grid-cols-3">
        <Card className="xl:col-span-2">
          <CardHeader className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
            <div>
              <CardTitle className="text-xl">{selectedStock?.name ?? "종목을 선택해 주세요"}</CardTitle>
              <CardDescription>
                {selectedStock ? `${selectedStock.symbol} · ${selectedStock.market}` : "선택된 종목이 없습니다."}
              </CardDescription>
            </div>

            {selectedStock && (
              <div
                className={cn(
                  "inline-flex items-center gap-1 rounded-full px-3 py-1 text-sm font-semibold",
                  isPositive
                    ? "bg-brand-50 text-brand-700 dark:bg-brand-900/35 dark:text-brand-300"
                    : "bg-red-50 text-red-700 dark:bg-red-900/30 dark:text-red-300",
                )}
              >
                {isPositive ? <TrendingUp className="h-4 w-4" /> : <TrendingDown className="h-4 w-4" />}
                {isPositive ? "+" : ""}
                {selectedStock.change.toLocaleString()} ({isPositive ? "+" : ""}
                {selectedStock.changeRate.toFixed(2)}%)
              </div>
            )}
          </CardHeader>
          <CardContent>
            {overviewError ? (
              <p className="text-sm text-red-600">{overviewError}</p>
            ) : !isKisVerified ? (
              <p className="text-sm text-muted-foreground">상단에서 API 키 검증을 완료해 주세요.</p>
            ) : isLoadingOverview && !selectedStock ? (
              <p className="text-sm text-muted-foreground">종목 데이터를 불러오는 중입니다...</p>
            ) : selectedStock ? (
              <>
                <p className="mb-4 text-3xl font-extrabold tracking-tight">{formatPrice(selectedStock.currentPrice)}</p>
                {effectivePriceSourceLabel && selectedOverviewMeta ? (
                  <div className="mb-4 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
                    <span className="rounded-full border border-brand-200 bg-brand-50 px-2 py-1 text-brand-700">
                      현재가 소스: {effectivePriceSourceLabel}
                    </span>
                    <span className="rounded-full border border-border px-2 py-1">
                      구간: {getMarketPhaseLabel(selectedOverviewMeta.marketPhase)}
                    </span>
                    <span className="rounded-full border border-border px-2 py-1">
                      조회시각: {new Date(selectedOverviewMeta.fetchedAt).toLocaleTimeString("ko-KR")}
                    </span>
                  </div>
                ) : null}
                <StockLineChart candles={chartCandles} />
                {realtimeError ? <p className="mt-3 text-xs text-red-600">{realtimeError}</p> : null}
              </>
            ) : (
              <p className="text-sm text-muted-foreground">종목을 선택하면 시세와 차트가 표시됩니다.</p>
            )}
          </CardContent>
        </Card>

        <div className="space-y-4">
          <Card>
            <CardHeader>
              <CardTitle className="text-base">핵심 지표</CardTitle>
            </CardHeader>
            <CardContent className="grid gap-2">
              <PriceStat label="시가" value={formatPrice(selectedStock?.open ?? 0)} />
              <PriceStat label="고가" value={formatPrice(selectedStock?.high ?? 0)} />
              <PriceStat label="저가" value={formatPrice(selectedStock?.low ?? 0)} />
              <PriceStat label="전일 종가" value={formatPrice(selectedStock?.prevClose ?? 0)} />
              <PriceStat label="누적 거래량" value={formatVolume(selectedStock?.volume ?? 0)} />
            </CardContent>
          </Card>

          <Card>
            <CardHeader>
              <CardTitle className="text-base">연동 상태</CardTitle>
            </CardHeader>
            <CardContent className="space-y-3 text-sm text-muted-foreground">
              <div className="flex items-center gap-2">
                <ShieldCheck className="h-4 w-4 text-brand-500" />
                <p>국내주식 {tradingEnv === "real" ? "실전" : "모의"}투자 API 연결 완료</p>
              </div>
              <div className="flex items-center gap-2">
                <Activity className={cn("h-4 w-4", isRealtimeConnected ? "text-brand-500" : "text-muted-foreground")} />
                <p>
                  실시간 체결가 연결 상태:{" "}
                  {isRealtimeConnected ? "연결됨 (WebSocket)" : "대기 중 (일봉 차트 표시)"}
                </p>
              </div>
              <div className="flex items-center gap-2">
                <Activity className="h-4 w-4 text-muted-foreground" />
                <p>마지막 실시간 수신: {lastRealtimeTickAt ? new Date(lastRealtimeTickAt).toLocaleTimeString("ko-KR") : "수신 전"}</p>
              </div>
              <div className="flex items-center gap-2">
                <Activity className="h-4 w-4 text-muted-foreground" />
                <p>실시간 틱 수신 수: {realtimeTickCount.toLocaleString("ko-KR")}건</p>
              </div>
              <div className="flex items-center gap-2">
                <Activity className="h-4 w-4 text-brand-500" />
                <p>다음 단계: 주문/리스크 제어 API 연결</p>
              </div>
            </CardContent>
          </Card>
        </div>
      </section>
    </div>
  );
}
