<script setup lang="ts">
/**
 * 서버 API 학습 데모 페이지
 * 모든 케이스를 브라우저에서 직접 테스트할 수 있습니다.
 */

// ── 케이스 1: 기본 GET ────────────────────────────────────────
// useFetch는 SSR에서 서버 측 호출 → 클라이언트에서 재호출 없이 데이터 hydration
const { data: hello } = await useFetch("/api/01-hello");

// ── 케이스 2: POST + readBody ─────────────────────────────────
const newUserName = ref("");
const newUserEmail = ref("");
const createdUser = ref<{ id: string; name: string; email: string; createdAt: string } | null>(null);
const createUserError = ref<string | null>(null);

async function createUser() {
  createdUser.value = null;
  createUserError.value = null;
  try {
    createdUser.value = await $fetch("/api/02-users", {
      method: "POST",
      body: { name: newUserName.value, email: newUserEmail.value },
    });
  } catch (err: unknown) {
    if (err && typeof err === "object" && "statusMessage" in err) {
      createUserError.value = String((err as { statusMessage: string }).statusMessage);
    }
  }
}

// ── 케이스 3: getQuery 쿼리 파라미터 (반응형) ─────────────────
// query에 ref를 직접 전달 → keyword/page 변경 시 자동 재요청
const keyword = ref("Nuxt");
const page = ref(1);
const { data: searchResult } = await useFetch("/api/03-search", {
  query: { keyword, page, limit: 3 },
});

// ── 케이스 4: 동적 라우트 파라미터 ──────────────────────────
// URL을 함수로 감싸야 userId 변경 시 자동 재요청
const userId = ref("1");
const { data: user, error: userError } = await useFetch(
  () => `/api/04-users/${userId.value}`
);

// ── 케이스 5: HTTP 메서드 분기 (GET / PUT / DELETE) ───────────
const itemId = ref("1");
const { data: item, error: itemError, refresh: refreshItem } = await useFetch(
  () => `/api/05-items/${itemId.value}`
);

// PUT: 아이템 done 토글
async function toggleItem() {
  if (!item.value) return;
  try {
    await $fetch(`/api/05-items/${itemId.value}`, {
      method: "PUT",
      body: { done: !(item.value as { done: boolean }).done },
    });
    await refreshItem(); // 변경 후 최신 데이터 재조회
  } catch (err: unknown) {
    alert(getErrorMessage(err));
  }
}

// DELETE: 아이템 삭제 후 id 1로 복귀
async function deleteItem() {
  try {
    await $fetch(`/api/05-items/${itemId.value}`, { method: "DELETE" });
    itemId.value = "1";
    await refreshItem();
  } catch (err: unknown) {
    alert(getErrorMessage(err));
  }
}

// ── 케이스 6: createError 에러 처리 ──────────────────────────
// 에러 타입별로 다른 응답 확인
type ErrorType = "notfound" | "forbidden" | "custom" | "random";
const errorType = ref<ErrorType>("notfound");
const errorResult = ref<{ result: string } | null>(null);
const errorResponse = ref<{
  statusCode: number;
  statusMessage: string;
  data?: unknown;
} | null>(null);

async function triggerError() {
  errorResult.value = null;
  errorResponse.value = null;
  try {
    const data = await $fetch<{ result: string }>(
      `/api/06-error-handling?type=${errorType.value}`
    );
    errorResult.value = data; // 성공 (random의 경우 50% 확률)
  } catch (err: unknown) {
    if (err && typeof err === "object" && "statusCode" in err) {
      errorResponse.value = err as {
        statusCode: number;
        statusMessage: string;
        data?: unknown;
      };
    }
  }
}

// ── 케이스 7: server/utils auto-import + 헤더 ─────────────────
// Authorization 헤더 유무에 따라 응답이 다름 (가격 포함 여부)
const useAuthToken = ref(false);
const authToken = "my-token-12345";

const { data: products, refresh: refreshProducts } = await useFetch(
  "/api/07-products",
  {
    headers: computed(() =>
      useAuthToken.value ? { Authorization: `Bearer ${authToken}` } : {}
    ),
  }
);

// useAuthToken 변경 시 수동 재요청 (headers는 반응형 자동 감지 안 됨)
watch(useAuthToken, () => refreshProducts());

// ── 케이스 8: server/routes/ 웹훅 ────────────────────────────
const webhookSecret = ref("my-secret");
const webhookEvent = ref("push");
const webhookRepo = ref("my-project");
const webhookResult = ref<{ received: boolean } | null>(null);
const webhookError = ref<string | null>(null);
const webhookLoading = ref(false);

async function sendWebhook() {
  webhookResult.value = null;
  webhookError.value = null;
  webhookLoading.value = true;
  try {
    webhookResult.value = await $fetch("/08-webhook", {
      method: "POST",
      headers: { "X-Webhook-Secret": webhookSecret.value },
      body: { event: webhookEvent.value, repo: webhookRepo.value },
    });
  } catch (err: unknown) {
    webhookError.value = getErrorMessage(err);
  } finally {
    webhookLoading.value = false;
  }
}

// ── 공통 유틸 ─────────────────────────────────────────────────
function getErrorMessage(err: unknown): string {
  if (err && typeof err === "object") {
    if ("statusMessage" in err)
      return `${(err as { statusCode: number }).statusCode}: ${(err as { statusMessage: string }).statusMessage}`;
    if ("message" in err) return String((err as { message: string }).message);
  }
  return "알 수 없는 오류";
}
</script>

<template>
  <div class="p-6 space-y-6 max-w-2xl mx-auto font-sans">
    <div class="border-b pb-4">
      <h1 class="text-2xl font-bold">서버 API 학습 데모</h1>
      <p class="text-sm text-gray-500 mt-1">
        각 섹션에서 server/api/ 케이스를 직접 테스트할 수 있습니다.
      </p>
    </div>

    <!-- ── 케이스 1: 기본 GET ── -->
    <section class="border rounded-lg p-4">
      <div class="flex items-center gap-2 mb-3">
        <span class="text-xs font-mono bg-blue-100 text-blue-700 px-2 py-0.5 rounded">GET /api/01-hello</span>
        <h2 class="font-semibold">케이스 1: 기본 GET</h2>
      </div>
      <p class="text-xs text-gray-500 mb-2">
        <code class="bg-gray-100 px-1 rounded">useFetch</code>는 SSR 환경에서 서버 측에서 미리 호출되어 클라이언트로 hydration됩니다.
      </p>
      <pre class="text-sm bg-gray-50 p-3 rounded overflow-auto">{{ hello }}</pre>
    </section>

    <!-- ── 케이스 2: POST + readBody ── -->
    <section class="border rounded-lg p-4">
      <div class="flex items-center gap-2 mb-3">
        <span class="text-xs font-mono bg-green-100 text-green-700 px-2 py-0.5 rounded">POST /api/02-users</span>
        <h2 class="font-semibold">케이스 2: POST + readBody</h2>
      </div>
      <p class="text-xs text-gray-500 mb-3">
        이벤트 핸들러 안에서 <code class="bg-gray-100 px-1 rounded">$fetch</code>로 POST 요청합니다. name/email을 비워두면 400 에러가 반환됩니다.
      </p>
      <div class="space-y-2 mb-3">
        <input v-model="newUserName" placeholder="이름 (필수)" class="block w-full border px-2 py-1.5 rounded text-sm" />
        <input v-model="newUserEmail" placeholder="이메일 (필수)" class="block w-full border px-2 py-1.5 rounded text-sm" />
      </div>
      <button class="bg-green-500 text-white px-4 py-1.5 rounded text-sm hover:bg-green-600" @click="createUser">
        사용자 생성
      </button>
      <pre v-if="createdUser" class="text-sm bg-green-50 text-green-800 p-3 rounded mt-3 overflow-auto">{{ createdUser }}</pre>
      <p v-if="createUserError" class="text-red-500 text-sm mt-2">⚠ {{ createUserError }}</p>
    </section>

    <!-- ── 케이스 3: getQuery ── -->
    <section class="border rounded-lg p-4">
      <div class="flex items-center gap-2 mb-3">
        <span class="text-xs font-mono bg-blue-100 text-blue-700 px-2 py-0.5 rounded">GET /api/03-search</span>
        <h2 class="font-semibold">케이스 3: getQuery 쿼리 파라미터</h2>
      </div>
      <p class="text-xs text-gray-500 mb-3">
        <code class="bg-gray-100 px-1 rounded">useFetch</code>의 <code class="bg-gray-100 px-1 rounded">query</code>에 ref를 전달하면 값 변경 시 자동으로 재요청합니다.
      </p>
      <div class="flex gap-2 mb-3 flex-wrap">
        <input v-model="keyword" placeholder="키워드" class="border px-2 py-1.5 rounded text-sm flex-1 min-w-24" />
        <div class="flex gap-1 items-center">
          <button
            v-for="p in [1, 2]" :key="p"
            class="border px-3 py-1.5 rounded text-sm"
            :class="page === p ? 'bg-blue-500 text-white' : 'hover:bg-gray-50'"
            @click="page = p"
          >
            {{ p }}페이지
          </button>
        </div>
      </div>
      <pre class="text-sm bg-gray-50 p-3 rounded overflow-auto">{{ searchResult }}</pre>
    </section>

    <!-- ── 케이스 4: 동적 라우트 ── -->
    <section class="border rounded-lg p-4">
      <div class="flex items-center gap-2 mb-3">
        <span class="text-xs font-mono bg-blue-100 text-blue-700 px-2 py-0.5 rounded">GET /api/04-users/:id</span>
        <h2 class="font-semibold">케이스 4: 동적 라우트 파라미터</h2>
      </div>
      <p class="text-xs text-gray-500 mb-3">
        URL을 함수 <code class="bg-gray-100 px-1 rounded">() => `/api/04-users/${userId.value}`</code>로 감싸면 변경 시 자동 재요청됩니다. ID 999는 404를 반환합니다.
      </p>
      <div class="flex gap-2 mb-3 flex-wrap">
        <button
          v-for="id in ['1', '2', '3', '999']" :key="id"
          class="border px-3 py-1.5 rounded text-sm"
          :class="userId === id ? 'bg-blue-500 text-white' : 'hover:bg-gray-50'"
          @click="userId = id"
        >
          ID: {{ id }}
        </button>
      </div>
      <pre v-if="user" class="text-sm bg-gray-50 p-3 rounded overflow-auto">{{ user }}</pre>
      <div v-if="userError" class="bg-red-50 border border-red-200 rounded p-3 mt-2">
        <p class="text-red-600 text-sm font-semibold">{{ userError.statusCode }} 에러</p>
        <p class="text-red-500 text-xs">{{ userError.statusMessage }}</p>
      </div>
    </section>

    <!-- ── 케이스 5: HTTP 메서드 분기 ── -->
    <section class="border rounded-lg p-4">
      <div class="flex items-center gap-2 mb-3">
        <span class="text-xs font-mono bg-purple-100 text-purple-700 px-2 py-0.5 rounded">GET|PUT|DELETE /api/05-items/:id</span>
        <h2 class="font-semibold">케이스 5: HTTP 메서드 분기</h2>
      </div>
      <p class="text-xs text-gray-500 mb-3">
        파일명에 메서드 접미사가 없으면 모든 메서드를 수신합니다. <code class="bg-gray-100 px-1 rounded">event.method</code>로 분기 처리합니다.
        ID 999는 404를 반환합니다.
      </p>
      <div class="flex gap-2 mb-3 flex-wrap">
        <button
          v-for="id in ['1', '2', '999']" :key="id"
          class="border px-3 py-1.5 rounded text-sm"
          :class="itemId === id ? 'bg-purple-500 text-white' : 'hover:bg-gray-50'"
          @click="itemId = id"
        >
          ID: {{ id }}
        </button>
      </div>
      <div v-if="item" class="bg-gray-50 rounded p-3 mb-3 flex items-center justify-between">
        <div>
          <pre class="text-sm overflow-auto">{{ item }}</pre>
        </div>
        <div class="flex gap-2 ml-4 shrink-0">
          <button
            class="text-xs bg-yellow-400 hover:bg-yellow-500 text-white px-2 py-1 rounded"
            @click="toggleItem"
          >
            done 토글 (PUT)
          </button>
          <button
            class="text-xs bg-red-500 hover:bg-red-600 text-white px-2 py-1 rounded"
            @click="deleteItem"
          >
            삭제 (DELETE)
          </button>
        </div>
      </div>
      <div v-if="itemError" class="bg-red-50 border border-red-200 rounded p-3">
        <p class="text-red-600 text-sm font-semibold">{{ itemError.statusCode }} 에러</p>
        <p class="text-red-500 text-xs">{{ itemError.statusMessage }}</p>
      </div>
    </section>

    <!-- ── 케이스 6: createError ── -->
    <section class="border rounded-lg p-4">
      <div class="flex items-center gap-2 mb-3">
        <span class="text-xs font-mono bg-red-100 text-red-700 px-2 py-0.5 rounded">GET /api/06-error-handling?type=</span>
        <h2 class="font-semibold">케이스 6: createError 에러 처리</h2>
      </div>
      <p class="text-xs text-gray-500 mb-3">
        에러 타입을 선택하고 요청해보세요. <code class="bg-gray-100 px-1 rounded">error.value.statusCode</code>와 <code class="bg-gray-100 px-1 rounded">error.value.data</code>로 세부 정보에 접근합니다.
        <span class="text-orange-500">random은 50% 확률로 성공/실패합니다.</span>
      </p>
      <div class="flex gap-2 mb-3 flex-wrap">
        <button
          v-for="t in ['notfound', 'forbidden', 'custom', 'random'] as const" :key="t"
          class="border px-3 py-1.5 rounded text-sm"
          :class="errorType === t ? 'bg-red-500 text-white' : 'hover:bg-gray-50'"
          @click="errorType = t"
        >
          {{ t }}
        </button>
      </div>
      <div class="mb-3 text-xs text-gray-400 bg-gray-50 rounded p-2">
        <template v-if="errorType === 'notfound'">→ <code>statusCode: 404</code> 반환</template>
        <template v-else-if="errorType === 'forbidden'">→ <code>statusCode: 403</code> + <code>data: &#123; required, current &#125;</code> 반환</template>
        <template v-else-if="errorType === 'custom'">→ <code>statusCode: 422</code> + <code>data.fields</code> 반환 (폼 유효성 에러 패턴)</template>
        <template v-else-if="errorType === 'random'">→ 성공 시 <code>&#123; result: "성공!" &#125;</code>, 실패 시 <code>statusCode: 500</code></template>
      </div>
      <button class="bg-red-500 text-white px-4 py-1.5 rounded text-sm hover:bg-red-600" @click="triggerError">
        에러 요청 보내기
      </button>
      <pre v-if="errorResult" class="text-sm bg-green-50 text-green-800 p-3 rounded mt-3 overflow-auto">{{ errorResult }}</pre>
      <div v-if="errorResponse" class="bg-red-50 border border-red-200 rounded p-3 mt-3">
        <p class="text-red-600 text-sm font-semibold mb-1">{{ errorResponse.statusCode }}: {{ errorResponse.statusMessage }}</p>
        <pre v-if="errorResponse.data" class="text-xs text-red-500 overflow-auto">data: {{ errorResponse.data }}</pre>
      </div>
    </section>

    <!-- ── 케이스 7: server/utils + 헤더 ── -->
    <section class="border rounded-lg p-4">
      <div class="flex items-center gap-2 mb-3">
        <span class="text-xs font-mono bg-blue-100 text-blue-700 px-2 py-0.5 rounded">GET /api/07-products</span>
        <h2 class="font-semibold">케이스 7: getHeader + server/utils auto-import</h2>
      </div>
      <p class="text-xs text-gray-500 mb-3">
        <code class="bg-gray-100 px-1 rounded">Authorization</code> 헤더 유무에 따라 응답이 달라집니다.
        <code class="bg-gray-100 px-1 rounded">server/utils/response.ts</code>의 헬퍼가 import 없이 사용됩니다.
      </p>
      <label class="flex items-center gap-2 mb-3 cursor-pointer select-none">
        <div
          class="w-10 h-5 rounded-full transition-colors"
          :class="useAuthToken ? 'bg-blue-500' : 'bg-gray-300'"
          @click="useAuthToken = !useAuthToken"
        >
          <div
            class="w-5 h-5 bg-white rounded-full shadow transition-transform"
            :class="useAuthToken ? 'translate-x-5' : 'translate-x-0'"
          />
        </div>
        <span class="text-sm">Authorization 헤더 포함</span>
        <code v-if="useAuthToken" class="text-xs bg-blue-50 text-blue-600 px-1 rounded">Bearer {{ authToken }}</code>
      </label>
      <pre class="text-sm bg-gray-50 p-3 rounded overflow-auto">{{ products }}</pre>
      <p class="text-xs text-gray-400 mt-1">
        토큰 있을 때: 가격(price) 포함 + authenticated: true<br />
        토큰 없을 때: 가격 숨김 + authenticated: false
      </p>
    </section>

    <!-- ── 케이스 8: server/routes/ 웹훅 ── -->
    <section class="border rounded-lg p-4">
      <div class="flex items-center gap-2 mb-3">
        <span class="text-xs font-mono bg-orange-100 text-orange-700 px-2 py-0.5 rounded">POST /08-webhook</span>
        <h2 class="font-semibold">케이스 8: server/routes/ (접두사 없음)</h2>
      </div>
      <p class="text-xs text-gray-500 mb-3">
        <code class="bg-gray-100 px-1 rounded">server/routes/</code>는 <code class="bg-gray-100 px-1 rounded">/api</code> 없이 URL을 직접 지정합니다.
        시크릿을 "my-secret" 이외로 바꾸면 401 에러가 반환됩니다.
      </p>
      <div class="space-y-2 mb-3">
        <div class="flex items-center gap-2">
          <label class="text-xs w-28 shrink-0 text-gray-600">X-Webhook-Secret</label>
          <input v-model="webhookSecret" class="border px-2 py-1.5 rounded text-sm flex-1" placeholder="my-secret" />
        </div>
        <div class="flex items-center gap-2">
          <label class="text-xs w-28 shrink-0 text-gray-600">event</label>
          <input v-model="webhookEvent" class="border px-2 py-1.5 rounded text-sm flex-1" placeholder="push" />
        </div>
        <div class="flex items-center gap-2">
          <label class="text-xs w-28 shrink-0 text-gray-600">repo</label>
          <input v-model="webhookRepo" class="border px-2 py-1.5 rounded text-sm flex-1" placeholder="my-project" />
        </div>
      </div>
      <button
        class="bg-orange-500 text-white px-4 py-1.5 rounded text-sm disabled:opacity-50 hover:bg-orange-600"
        :disabled="webhookLoading"
        @click="sendWebhook"
      >
        {{ webhookLoading ? "전송 중..." : "웹훅 전송" }}
      </button>
      <pre v-if="webhookResult" class="text-sm bg-green-50 text-green-800 p-3 rounded mt-3 overflow-auto">{{ webhookResult }}</pre>
      <p v-if="webhookError" class="text-red-500 text-sm mt-2">⚠ {{ webhookError }}</p>
    </section>
  </div>
</template>
