import { createApp, onMounted, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js' // 브라우저에서 바로 사용할 수 있는 Vue ESM 번들을 가져온다.

const appConfig = window.APP_CONFIG ?? {} // index.html에서 선언한 전역 설정 객체를 읽고 없으면 빈 객체를 사용한다.
const apiHost = appConfig.apiHost || 'http://127.0.0.1' // 전역 설정의 호스트 값을 읽고 없으면 기본 호스트를 사용한다.
const apiPort = appConfig.apiPort || '8000' // 전역 설정의 포트 값을 읽고 없으면 기본 포트를 사용한다.
const API_BASE_URL = `${apiHost}:${apiPort}/api` // 실제 fetch 요청에 사용할 기본 API 주소를 전역 설정과 기본값으로 조합한다.

createApp({ // 루트 Vue 애플리케이션을 생성한다.
  setup() { // 컴포지션 API 기반의 설정 함수를 정의한다.
    const messages = ref([]) // 화면에 표시할 메시지 목록 상태를 만든다.
    const newContent = ref('') // 새 메시지 입력값 상태를 만든다.
    const editingId = ref(null) // 현재 수정 중인 메시지의 id 상태를 만든다.
    const editingContent = ref('') // 수정 입력창에 바인딩할 메시지 내용 상태를 만든다.
    const loading = ref(true) // API 요청 중인지 나타내는 상태를 만든다.
    const saving = ref(false) // 생성이나 수정 요청 중인지 나타내는 상태를 만든다.
    const error = ref('') // 에러 메시지를 저장할 상태를 만든다.

    const extractErrorMessage = async (response, fallbackMessage) => { // 실패 응답에서 사용자용 에러 문구를 추출하는 함수를 정의한다.
      try { // JSON 응답 본문을 우선 해석해 보도록 시도한다.
        const data = await response.json() // 응답 본문을 JSON으로 변환한다.
        if (typeof data.detail === 'string' && data.detail.trim()) { // FastAPI detail 문자열이 있는지 확인한다.
          return data.detail // 서버가 내려준 상세 오류 문구를 그대로 반환한다.
        } // detail 확인 블록을 닫는다.
      } catch (caughtError) { // JSON 파싱 자체가 실패한 경우를 처리한다.
        console.debug('에러 응답 본문 파싱 실패', caughtError) // 디버깅을 위해 콘솔에 파싱 실패 정보를 남긴다.
      } // 에러 본문 파싱 시도 블록을 닫는다.
      return fallbackMessage // 상세 오류를 얻지 못한 경우 기본 문구를 반환한다.
    } // 에러 문구 추출 함수를 마무리한다.

    const loadMessages = async () => { // 백엔드에서 메시지 목록을 읽어오는 비동기 함수를 정의한다.
      loading.value = true // 요청 시작 시 로딩 상태를 켠다.
      error.value = '' // 이전 에러 메시지를 비운다.
      try { // API 요청을 안전하게 시도한다.
        const response = await fetch(`${API_BASE_URL}/messages`) // FastAPI 목록 조회 API를 절대 경로로 호출한다.
        if (!response.ok) { // HTTP 상태 코드가 실패인 경우를 확인한다.
          throw new Error(await extractErrorMessage(response, '메시지 목록을 불러오지 못했습니다.')) // 사용자에게 보여줄 에러를 발생시킨다.
        } // HTTP 상태 확인 블록을 닫는다.
        const data = await response.json() // 응답 본문을 JSON으로 변환한다.
        messages.value = Array.isArray(data) ? data : [] // 응답이 배열이면 목록 상태에 저장하고 아니면 빈 배열로 대체한다.
      } catch (caughtError) { // 요청 실패 시 에러를 처리한다.
        error.value = caughtError instanceof Error ? caughtError.message : '알 수 없는 오류가 발생했습니다.' // 에러 객체를 사람이 읽을 수 있는 문자열로 바꿔 저장한다.
      } finally { // 성공 여부와 관계없이 마지막 정리 작업을 수행한다.
        loading.value = false // 요청 종료 후 로딩 상태를 끈다.
      } // 요청 처리 블록을 마무리한다.
    } // 목록 로딩 함수를 마무리한다.

    const createMessage = async () => { // 새 메시지를 생성하는 비동기 함수를 정의한다.
      const trimmedContent = newContent.value.trim() // 입력창 값의 앞뒤 공백을 제거한다.
      if (!trimmedContent) { // 공백 제거 후 빈 문자열인지 확인한다.
        error.value = '메시지를 입력해 주세요.' // 사용자에게 입력이 필요하다는 에러를 표시한다.
        return // 잘못된 입력이면 생성 요청을 보내지 않고 함수를 종료한다.
      } // 입력 검증 블록을 닫는다.
      saving.value = true // 생성 요청 중 상태를 켠다.
      error.value = '' // 이전 에러 메시지를 비운다.
      try { // 생성 API 요청을 안전하게 시도한다.
        const response = await fetch(`${API_BASE_URL}/messages`, { // 메시지 생성 API를 호출한다.
          method: 'POST', // HTTP 메서드를 POST로 지정한다.
          headers: { 'Content-Type': 'application/json' }, // JSON 본문 전송을 위한 헤더를 지정한다.
          body: JSON.stringify({ content: trimmedContent }), // 새 메시지 내용을 JSON 문자열로 변환해 보낸다.
        }) // fetch 요청 객체 정의를 마무리한다.
        if (!response.ok) { // HTTP 상태 코드가 실패인 경우를 확인한다.
          throw new Error(await extractErrorMessage(response, '메시지를 추가하지 못했습니다.')) // 사용자에게 보여줄 에러를 발생시킨다.
        } // HTTP 상태 확인 블록을 닫는다.
        newContent.value = '' // 생성 성공 후 새 메시지 입력창을 비운다.
        await loadMessages() // 최신 목록을 다시 불러와 화면에 반영한다.
      } catch (caughtError) { // 생성 요청 실패 시 에러를 처리한다.
        error.value = caughtError instanceof Error ? caughtError.message : '알 수 없는 오류가 발생했습니다.' // 에러 객체를 사람이 읽을 수 있는 문자열로 바꿔 저장한다.
      } finally { // 성공 여부와 관계없이 마지막 정리 작업을 수행한다.
        saving.value = false // 생성 요청 종료 후 저장 상태를 끈다.
      } // 요청 처리 블록을 마무리한다.
    } // 메시지 생성 함수를 마무리한다.

    const startEditing = (message) => { // 특정 메시지를 수정 모드로 전환하는 함수를 정의한다.
      editingId.value = message.id // 현재 수정 중인 메시지 id를 저장한다.
      editingContent.value = message.content // 현재 메시지 내용을 수정 입력창 상태에 복사한다.
      error.value = '' // 수정 시작 시 이전 에러 메시지를 비운다.
    } // 수정 시작 함수를 마무리한다.

    const cancelEditing = () => { // 수정 모드를 취소하는 함수를 정의한다.
      editingId.value = null // 수정 중인 메시지 id를 비운다.
      editingContent.value = '' // 수정 입력창 상태를 비운다.
    } // 수정 취소 함수를 마무리한다.

    const saveMessage = async () => { // 수정 중인 메시지를 저장하는 비동기 함수를 정의한다.
      const trimmedContent = editingContent.value.trim() // 수정 입력값의 앞뒤 공백을 제거한다.
      if (!trimmedContent) { // 공백 제거 후 빈 문자열인지 확인한다.
        error.value = '수정할 메시지를 입력해 주세요.' // 사용자에게 입력이 필요하다는 에러를 표시한다.
        return // 잘못된 입력이면 수정 요청을 보내지 않고 함수를 종료한다.
      } // 입력 검증 블록을 닫는다.
      if (editingId.value === null) { // 실제 수정 대상 id가 없는 비정상 상태인지 확인한다.
        error.value = '수정할 메시지를 찾을 수 없습니다.' // 사용자에게 수정 대상이 없다는 에러를 표시한다.
        return // 잘못된 상태이면 함수를 종료한다.
      } // 수정 대상 확인 블록을 닫는다.
      saving.value = true // 수정 요청 중 상태를 켠다.
      error.value = '' // 이전 에러 메시지를 비운다.
      try { // 수정 API 요청을 안전하게 시도한다.
        const response = await fetch(`${API_BASE_URL}/messages/${editingId.value}`, { // 메시지 수정 API를 호출한다.
          method: 'PUT', // HTTP 메서드를 PUT으로 지정한다.
          headers: { 'Content-Type': 'application/json' }, // JSON 본문 전송을 위한 헤더를 지정한다.
          body: JSON.stringify({ content: trimmedContent }), // 수정할 메시지 내용을 JSON 문자열로 변환해 보낸다.
        }) // fetch 요청 객체 정의를 마무리한다.
        if (!response.ok) { // HTTP 상태 코드가 실패인 경우를 확인한다.
          throw new Error(await extractErrorMessage(response, '메시지를 수정하지 못했습니다.')) // 사용자에게 보여줄 에러를 발생시킨다.
        } // HTTP 상태 확인 블록을 닫는다.
        cancelEditing() // 수정 성공 후 수정 모드를 종료한다.
        await loadMessages() // 최신 목록을 다시 불러와 화면에 반영한다.
      } catch (caughtError) { // 수정 요청 실패 시 에러를 처리한다.
        error.value = caughtError instanceof Error ? caughtError.message : '알 수 없는 오류가 발생했습니다.' // 에러 객체를 사람이 읽을 수 있는 문자열로 바꿔 저장한다.
      } finally { // 성공 여부와 관계없이 마지막 정리 작업을 수행한다.
        saving.value = false // 수정 요청 종료 후 저장 상태를 끈다.
      } // 요청 처리 블록을 마무리한다.
    } // 메시지 저장 함수를 마무리한다.

    const removeMessage = async (messageId) => { // 특정 메시지를 삭제하는 비동기 함수를 정의한다.
      error.value = '' // 삭제 시작 시 이전 에러 메시지를 비운다.
      try { // 삭제 API 요청을 안전하게 시도한다.
        const response = await fetch(`${API_BASE_URL}/messages/${messageId}`, { method: 'DELETE' }) // 메시지 삭제 API를 호출한다.
        if (!response.ok) { // HTTP 상태 코드가 실패인 경우를 확인한다.
          throw new Error(await extractErrorMessage(response, '메시지를 삭제하지 못했습니다.')) // 사용자에게 보여줄 에러를 발생시킨다.
        } // HTTP 상태 확인 블록을 닫는다.
        if (editingId.value === messageId) { // 현재 수정 중인 항목이 삭제 대상과 같은지 확인한다.
          cancelEditing() // 삭제 대상이 수정 중이었다면 수정 모드를 종료한다.
        } // 수정 중 항목 삭제 처리 블록을 닫는다.
        await loadMessages() // 최신 목록을 다시 불러와 화면에 반영한다.
      } catch (caughtError) { // 삭제 요청 실패 시 에러를 처리한다.
        error.value = caughtError instanceof Error ? caughtError.message : '알 수 없는 오류가 발생했습니다.' // 에러 객체를 사람이 읽을 수 있는 문자열로 바꿔 저장한다.
      } // 요청 처리 블록을 마무리한다.
    } // 메시지 삭제 함수를 마무리한다.

    onMounted(() => { // 컴포넌트가 화면에 마운트된 뒤 실행할 작업을 등록한다.
      loadMessages() // 첫 화면 진입 시 메시지 목록을 바로 불러온다.
    }) // 마운트 훅 등록을 마무리한다.

    return { messages, newContent, editingId, editingContent, loading, saving, error, loadMessages, createMessage, startEditing, cancelEditing, saveMessage, removeMessage } // 템플릿에서 사용할 상태와 함수를 반환한다.
  }, // setup 함수 정의를 닫는다.
  template: ` // 화면에 렌더링할 템플릿을 정의한다.
    <main style="min-height: 100vh; padding: 40px 16px; background: #f5f7fb; font-family: Arial, sans-serif;"> <!-- 전체 화면 배경과 패딩을 지정한다. -->
      <section style="width: 100%; max-width: 760px; margin: 0 auto; padding: 32px; border-radius: 20px; background: #ffffff; box-shadow: 0 12px 32px rgba(15, 23, 42, 0.12);"> <!-- 카드형 예제 영역을 구성한다. -->
        <h1 style="margin: 0 0 12px; color: #111827; text-align: center;">SQLite + FastAPI + Vue CRUD</h1> <!-- 예제 제목을 표시한다. -->
        <p style="margin: 0 0 24px; color: #6b7280; text-align: center;">DB에 저장된 메시지를 조회하고 추가, 수정, 삭제하는 최소 CRUD 예제입니다.</p> <!-- 화면 설명 문구를 표시한다. -->
        <form @submit.prevent="createMessage" style="display: flex; gap: 12px; margin-bottom: 20px;"> <!-- 새 메시지 생성 폼을 구성한다. -->
          <input v-model="newContent" type="text" placeholder="새 메시지를 입력하세요." style="flex: 1; padding: 12px 14px; border: 1px solid #d1d5db; border-radius: 10px; font-size: 14px;" /> <!-- 새 메시지 입력창을 렌더링한다. -->
          <button type="submit" :disabled="saving" style="padding: 12px 18px; border: none; border-radius: 10px; background: #111827; color: #ffffff; cursor: pointer;">{{ saving ? '저장 중...' : '추가' }}</button> <!-- 메시지 생성 버튼을 렌더링한다. -->
        </form> <!-- 새 메시지 생성 폼을 닫는다. -->
        <div style="display: flex; justify-content: flex-end; margin-bottom: 16px;"> <!-- 새로고침 버튼 정렬 영역을 구성한다. -->
          <button type="button" @click="loadMessages" :disabled="loading" style="padding: 10px 14px; border: 1px solid #d1d5db; border-radius: 10px; background: #ffffff; color: #111827; cursor: pointer;">다시 불러오기</button> <!-- 목록 재조회 버튼을 렌더링한다. -->
        </div> <!-- 새로고침 버튼 정렬 영역을 닫는다. -->
        <p v-if="loading" style="margin: 0; color: #2563eb; text-align: center;">불러오는 중...</p> <!-- 로딩 중일 때 안내 문구를 표시한다. -->
        <p v-else-if="error" style="margin: 0 0 16px; color: #dc2626; text-align: center;">{{ error }}</p> <!-- 에러가 있을 때 에러 문구를 표시한다. -->
        <p v-if="!loading && !messages.length" style="margin: 0; color: #6b7280; text-align: center;">저장된 메시지가 없습니다.</p> <!-- 목록이 비어 있을 때 안내 문구를 표시한다. -->
        <ul v-if="!loading && messages.length" style="display: grid; gap: 12px; padding: 0; margin: 0; list-style: none;"> <!-- 메시지 목록을 렌더링하는 리스트를 구성한다. -->
          <li v-for="message in messages" :key="message.id" style="padding: 16px; border: 1px solid #e5e7eb; border-radius: 14px; background: #f9fafb;"> <!-- 개별 메시지 카드 영역을 렌더링한다. -->
            <div v-if="editingId !== message.id" style="display: flex; align-items: center; justify-content: space-between; gap: 12px;"> <!-- 일반 보기 모드의 행 레이아웃을 구성한다. -->
              <div style="display: flex; flex-direction: column; gap: 6px;"> <!-- 메시지 메타 정보 영역을 구성한다. -->
                <strong style="color: #111827;">메시지 #{{ message.id }}</strong> <!-- 메시지 id를 표시한다. -->
                <span style="color: #374151; word-break: break-word;">{{ message.content }}</span> <!-- 메시지 내용을 표시한다. -->
              </div> <!-- 메시지 메타 정보 영역을 닫는다. -->
              <div style="display: flex; gap: 8px;"> <!-- 액션 버튼 묶음 영역을 구성한다. -->
                <button type="button" @click="startEditing(message)" style="padding: 10px 12px; border: none; border-radius: 10px; background: #2563eb; color: #ffffff; cursor: pointer;">수정</button> <!-- 수정 시작 버튼을 렌더링한다. -->
                <button type="button" @click="removeMessage(message.id)" style="padding: 10px 12px; border: none; border-radius: 10px; background: #dc2626; color: #ffffff; cursor: pointer;">삭제</button> <!-- 삭제 버튼을 렌더링한다. -->
              </div> <!-- 액션 버튼 묶음 영역을 닫는다. -->
            </div> <!-- 일반 보기 모드의 행 레이아웃을 닫는다. -->
            <div v-else style="display: grid; gap: 12px;"> <!-- 수정 모드의 레이아웃을 구성한다. -->
              <input v-model="editingContent" type="text" style="width: 100%; padding: 12px 14px; border: 1px solid #d1d5db; border-radius: 10px; font-size: 14px;" /> <!-- 수정 입력창을 렌더링한다. -->
              <div style="display: flex; gap: 8px; justify-content: flex-end;"> <!-- 수정 모드 버튼 정렬 영역을 구성한다. -->
                <button type="button" @click="cancelEditing" style="padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 10px; background: #ffffff; color: #111827; cursor: pointer;">취소</button> <!-- 수정 취소 버튼을 렌더링한다. -->
                <button type="button" @click="saveMessage" :disabled="saving" style="padding: 10px 12px; border: none; border-radius: 10px; background: #059669; color: #ffffff; cursor: pointer;">{{ saving ? '저장 중...' : '저장' }}</button> <!-- 수정 저장 버튼을 렌더링한다. -->
              </div> <!-- 수정 모드 버튼 정렬 영역을 닫는다. -->
            </div> <!-- 수정 모드의 레이아웃을 닫는다. -->
          </li> <!-- 개별 메시지 카드 영역을 닫는다. -->
        </ul> <!-- 메시지 목록 리스트를 닫는다. -->
      </section> <!-- 카드형 예제 영역을 닫는다. -->
    </main> <!-- 전체 화면 래퍼를 닫는다. -->
  `, // 템플릿 문자열 정의를 마무리한다.
}).mount('#app') // 생성한 Vue 앱을 app 요소에 마운트한다.
