<template>
  <div class="relative flex h-full w-full items-center justify-center">
    <div class="relative z-1 flex h-full w-full flex-col items-center justify-start gap-4 rounded-xl border-none p-4">
      <div
        class="flex w-full items-center justify-between gap-4 overflow-visible rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
      >
        <div class="flex flex-1 flex-wrap items-center gap-4 p-1">
          <FileCode :size="24" class="text-accent" />
          <div>
            <h1 class="m-0 text-2xl font-semibold tracking-tight text-main">{{ t('pages.scripts.title') }}</h1>
            <p class="m-0 text-sm text-secondary">{{ t('pages.scripts.description') }}</p>
          </div>
        </div>
        <div class="flex flex-wrap gap-3 overflow-visible">
          <CustomButton
            type="secondary"
            :icon="StoreIcon"
            :text="t('pages.scripts.marketplace.browseMarketplace')"
            @click="openMarketplace"
          />
          <CustomButton
            type="primary"
            :icon="FolderOpen"
            :text="t('pages.scripts.openScriptFolder')"
            @click="handleOpenScriptFolder"
          />
        </div>
      </div>
      <div
        class="flex w-full flex-row items-center justify-between gap-4 overflow-visible rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
      >
        <div class="flex flex-wrap gap-3 overflow-visible">
          <div class="flex max-w-[220px] min-w-[180px] flex-1 flex-col gap-1">
            <MultiSelect
              v-model:choosed="choosedCat"
              :zero-placeholder="t('pages.scripts.chooseScriptType')"
              :all-list="supportedScriptCategories"
            />
          </div>
          <CustomButton
            type="primary"
            :icon="Edit2Icon"
            :text="t('pages.scripts.editENVFile')"
            @click="openEditPage(['.env'])"
          />
        </div>
      </div>

      <!-- Plugin Grid -->
      <div
        class="relative flex h-full w-full flex-1 items-center justify-center overflow-hidden rounded-2xl border border-border-secondary p-4 shadow-md"
      >
        <div class="no-scrollbar h-full w-full overflow-auto rounded-sm">
          <div class="grid w-full grid-cols-[repeat(auto-fill,minmax(300px,1fr))] gap-5 border-none p-1 max-md:gap-4">
            <div
              v-for="(item, index) in scriptsList"
              :key="item.fileName + index"
              class="group/config-card relative flex min-h-[160px] cursor-pointer flex-col gap-6 overflow-hidden rounded-xl border border-border-secondary p-5 shadow-sm transition-all duration-fast ease-apple hover:border-2 hover:border-accent hover:shadow-md [.disabled]:opacity-80"
              :class="{
                disabled:
                  !item.enabled && item.category !== 'manualTrigger' && item.category !== 'uploader.advancedplist',
              }"
            >
              <div
                class="absolute right-1 bottom-0 flex h-[15px] w-auto items-center rounded-md bg-gray-400 px-2 py-1 text-[0.6rem] font-medium text-white"
              >
                {{ supportedScriptCategories.find(cat => cat.type === item.category)?.name || item.category }}
              </div>
              <div class="relative z-1 flex flex-1 items-start justify-between">
                <div
                  class="peer flex h-[40px] w-[40px] items-center justify-center rounded-lg border border-accent/10 text-accent transition-all duration-fast ease-apple group-hover/config-card:scale-105"
                >
                  <FileCode :size="20" />
                </div>
                <div
                  class="grid grid-cols-2 gap-1.5 opacity-0 transition-all duration-fast ease-apple group-hover/config-card:opacity-100"
                >
                  <button
                    class="action-btn"
                    :title="t('pages.scripts.editScript')"
                    @click.stop="openEditPage(item.filePath)"
                  >
                    <Pencil :size="14" />
                  </button>
                  <button
                    class="action-btn danger"
                    :title="t('pages.scripts.deleteScript')"
                    @click.stop="() => deleteConfig(item.filePath)"
                  >
                    <Trash2 :size="14" />
                  </button>
                  <button
                    class="action-btn bg-accent/50 text-white! hover:bg-accent!"
                    :title="t('pages.scripts.marketplace.shareScript')"
                    @click.stop="openShareDialog(item)"
                  >
                    <Share2Icon :size="14" />
                  </button>
                  <button
                    v-if="item.category === 'manualTrigger'"
                    class="action-btn bg-accent/50 text-white! hover:bg-accent!"
                    :title="t('pages.scripts.runScript')"
                    @click.stop="runScript(item.filePath)"
                  >
                    <Play :size="14" />
                  </button>

                  <button
                    v-if="item.category !== 'manualTrigger' && item.category !== 'uploader.advancedplist'"
                    class="action-btn border-none"
                    :class="{
                      'bg-success/50 hover:bg-success!': !item.enabled,
                      'bg-error/50 hover:bg-error!': item.enabled,
                    }"
                    :title="item.enabled ? t('pages.scripts.disableScript') : t('pages.scripts.enableScript')"
                    @click.stop="toggleScript(item.filePath)"
                  >
                    <template v-if="!item.enabled">
                      <CheckCircle2 :size="16" class="text-white" />
                    </template>
                    <template v-else>
                      <XIcon :size="16" class="text-white" />
                    </template>
                  </button>
                </div>
              </div>
              <div class="relative z-1 flex-1">
                <div class="mx-0 mt-0 mb-2 flex items-center text-base font-semibold tracking-tight text-main">
                  {{ item.fileName }}
                </div>

                <div class="mb-3 flex items-center gap-1.5 text-xs text-tertiary">
                  <div class="flex items-center gap-1">
                    <Clock :size="12" />
                    <span>{{ formatDate(item.mtimeMs) }}</span>
                  </div>
                  <div
                    v-if="item.enabled"
                    class="inline-flex items-center gap-1.5 rounded-2xl bg-accent/40 px-2 py-1 text-xs font-medium text-white transition-all duration-fast ease-standard"
                  >
                    <span>{{ t('pages.scripts.enabled') }}</span>
                  </div>
                  <div
                    v-else
                    class="inline-flex items-center gap-1.5 rounded-2xl bg-error/40 px-2 py-1 text-xs font-medium text-white transition-all duration-fast ease-standard"
                  >
                    <span>{{ t('pages.scripts.disabled') }}</span>
                  </div>
                </div>
              </div>
            </div>
            <div
              key="add-new"
              class="group/new relative flex min-h-[180px] cursor-pointer flex-col items-center justify-center gap-6 overflow-hidden rounded-xl border-2 border-dashed border-border p-5 shadow-sm transition-all duration-fast ease-apple hover:border-solid hover:border-accent hover:bg-surface hover:shadow-md"
              @click="openNewScriptsNameDialog"
            >
              <div class="flex flex-col items-center gap-3 transition-all duration-fast ease-apple">
                <div
                  class="flex h-[56px] w-[56px] items-center justify-center rounded-xl border-2 border-dashed border-border text-tertiary transition-all duration-fast ease-apple group-hover/new:scale-105 group-hover/new:border-solid group-hover/new:border-accent group-hover/new:bg-accent/5 group-hover/new:text-accent"
                >
                  <Plus :size="24" />
                </div>
                <div class="flex flex-col items-center gap-1">
                  <span class="text-base font-semibold text-secondary">{{ t('pages.scripts.addNew') }}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <CustomModal v-if="editorVisible" v-model:visible="editorVisible" :title="t('common.edit')">
      <Editor v-model="editorContent" language="javascript" />
      <template #footer>
        <CustomButton type="secondary" :text="t('common.cancel')" @click="editorVisible = false" />
        <CustomButton type="primary" :text="t('common.save')" @click="saveEditorContent" />
      </template>
    </CustomModal>

    <CustomModal
      v-if="newScriptNameVisible"
      v-model:visible="newScriptNameVisible"
      :title="t('pages.scripts.addNew')"
      height="auto"
      width="600px"
    >
      <div class="flex flex-col items-center justify-center gap-4 bg-bg-secondary p-6">
        <SettingCard class="w-full">
          <SingleSelect
            v-model="newScriptCategory"
            :title="t('pages.scripts.selectScriptType')"
            :key-list="supportedScriptCategories.map(cat => cat.type)"
            :fronticon="false"
            :placeholder="
              supportedScriptCategories.find(cat => cat.type === newScriptCategory)
                ? supportedScriptCategories.find(cat => cat.type === newScriptCategory)?.name
                : newScriptCategory
            "
          >
            <template #item="{ item }">
              {{
                supportedScriptCategories.find(cat => cat.type === item)
                  ? supportedScriptCategories.find(cat => cat.type === item)?.name
                  : item
              }}
            </template>
          </SingleSelect>
        </SettingCard>
        <SettingCard class="w-full">
          <CustomInput
            v-model="newScriptName"
            :title="t('pages.scripts.pleaseEnterScriptName')"
            placeholder="test.js"
          />
        </SettingCard>
      </div>
      <template #footer>
        <CustomButton type="secondary" :text="t('common.cancel')" @click="newScriptNameVisible = false" />
        <CustomButton type="primary" :text="t('common.confirm')" @click="handleNewScriptNameConfirm" />
      </template>
    </CustomModal>

    <CustomModal
      v-if="marketplaceVisible"
      v-model:visible="marketplaceVisible"
      :title="t('pages.scripts.marketplace.title')"
    >
      <div class="flex h-full w-full flex-col gap-4 p-4">
        <div class="flex flex-wrap items-center justify-between gap-4">
          <div class="relative flex flex-1 items-center">
            <SearchIcon class="absolute left-3 z-1 text-secondary" :size="18" />
            <input
              v-model="marketplaceSearch"
              type="text"
              class="w-full rounded-lg border border-border bg-bg-secondary px-10 py-2 text-sm text-main placeholder:text-secondary focus:border-accent focus:outline-none"
              :placeholder="t('pages.scripts.marketplace.searchPlaceholder')"
            />
          </div>
          <div class="flex items-center gap-2">
            <template v-if="githubAuth.isAuthenticated">
              <span class="text-sm text-secondary">
                {{ t('pages.scripts.marketplace.loggedInAs', { username: githubAuth.username }) }}
              </span>
              <CustomButton
                type="secondary"
                :icon="LogOutIcon"
                :text="t('pages.scripts.marketplace.logout')"
                @click="handleGitHubLogout"
              />
            </template>
            <template v-else>
              <CustomButton
                type="secondary"
                :icon="null"
                :text="t('pages.scripts.marketplace.loginWithGitHub')"
                @click="handleGitHubLogin"
              >
                <template #icon>
                  <BaseSvg name="GitHub" :size="18" color="black" />
                </template>
              </CustomButton>
            </template>
            <CustomButton
              type="secondary"
              :icon="ExternalLinkIcon"
              :text="t('pages.scripts.marketplace.openMarketplaceRepo')"
              @click="openMarketplaceRepo"
            />
          </div>
        </div>

        <div class="flex items-center gap-2">
          <MultiSelect
            v-model:choosed="marketplaceCategoryFilter"
            :zero-placeholder="t('pages.scripts.marketplace.allCategories')"
            :all-list="supportedScriptCategories"
          />
        </div>

        <div v-if="marketplaceLoading" class="flex flex-1 flex-col items-center justify-center gap-4">
          <div class="h-10 w-10 animate-spin rounded-full border-4 border-border border-t-accent" />
          <span class="text-sm text-secondary">{{ t('pages.scripts.marketplace.loadingScripts') }}</span>
        </div>

        <div v-else-if="marketplaceError" class="flex flex-1 flex-col items-center justify-center gap-4">
          <XCircleIcon :size="48" class="text-danger" />
          <span class="text-sm text-danger">{{ t('pages.scripts.marketplace.loadFailed') }}</span>
          <CustomButton
            type="primary"
            :icon="RefreshCwIcon"
            :text="t('pages.scripts.marketplace.retry')"
            @click="fetchMarketplaceScripts"
          />
        </div>

        <div v-else class="flex-1 overflow-hidden rounded-lg border border-border">
          <div class="no-scrollbar h-full overflow-auto p-4">
            <div
              v-if="filteredMarketplaceScripts.length === 0"
              class="flex h-full flex-col items-center justify-center gap-4"
            >
              <PackageIcon :size="48" class="text-secondary opacity-50" />
              <span class="text-sm text-secondary">{{ t('pages.scripts.marketplace.noScriptsFound') }}</span>
            </div>
            <div
              v-else-if="!scriptContentOfMarketplaceVisible"
              class="grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] gap-4"
            >
              <div
                v-for="script in filteredMarketplaceScripts"
                :key="script.downloadUrl"
                class="flex flex-col gap-3 rounded-xl border border-border-secondary p-4 transition-all hover:border-accent hover:shadow-md"
              >
                <div class="flex items-start justify-between">
                  <div class="flex items-center gap-2">
                    <FileCode :size="20" class="text-accent" />
                    <span class="font-semibold text-main">{{ script.name }}</span>
                  </div>
                  <span class="rounded bg-bg-tertiary px-2 py-0.5 text-xs text-secondary">v{{ script.version }}</span>
                </div>
                <p class="line-clamp-2 min-h-[40px] text-sm text-secondary">{{ script.description || '-' }}</p>
                <div class="flex items-center gap-2 text-xs text-tertiary">
                  <UserIcon :size="12" />
                  <span>{{ script.author }}</span>
                  <span class="mx-1">•</span>
                  <span class="rounded bg-gray-400 px-1.5 py-0.5 text-white">
                    {{ supportedScriptCategories.find(cat => cat.type === script.category)?.name || script.category }}
                  </span>
                </div>
                <div class="mt-auto flex gap-2 pt-2">
                  <button
                    class="flex w-full items-center justify-center gap-2 rounded-lg border border-border bg-accent/20 px-4 py-2 text-sm font-semibold text-accent"
                    @click="openScriptDetails(script.content || '')"
                  >
                    <Edit2Icon :size="16" />
                    {{ t('pages.scripts.marketplace.showScriptCode') }}
                  </button>
                  <button
                    v-if="!isScriptDownloaded(script)"
                    class="flex w-full items-center justify-center gap-2 rounded-lg bg-success/90 px-4 py-2 text-sm font-semibold text-white transition-all hover:bg-success disabled:opacity-50"
                    :disabled="downloadingScripts.has(script.downloadUrl)"
                    @click="downloadMarketplaceScript(script)"
                  >
                    <template v-if="downloadingScripts.has(script.downloadUrl)">
                      <div class="h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" />
                      {{ t('pages.scripts.marketplace.downloading') }}
                    </template>
                    <template v-else>
                      <DownloadIcon :size="16" />
                      {{ t('pages.scripts.marketplace.download') }}
                    </template>
                  </button>
                  <button
                    v-if="isScriptDownloaded(script)"
                    class="flex w-full items-center justify-center gap-2 rounded-lg border border-success bg-success/20 px-4 py-2 text-sm font-semibold text-success"
                    disabled
                  >
                    <CheckIcon :size="16" />
                    {{ t('pages.scripts.marketplace.downloaded') }}
                  </button>
                </div>
              </div>
            </div>
            <div v-else class="flex h-full flex-col gap-4">
              <Editor v-model="marketplaceScriptContent" language="javascript" :read-only="true" />
              <CustomButton
                class="mt-4"
                type="primary"
                :text="t('common.cancel')"
                @click="
                  () => {
                    scriptContentOfMarketplaceVisible = false
                    marketplaceScriptContent = ''
                  }
                "
              />
            </div>
          </div>
        </div>
      </div>
    </CustomModal>

    <CustomModal
      v-if="shareDialogVisible"
      v-model:visible="shareDialogVisible"
      :title="t('pages.scripts.marketplace.shareScript')"
      height="auto"
      width="500px"
    >
      <div class="flex flex-col gap-4 p-6">
        <div v-if="!githubAuth.isAuthenticated" class="flex flex-col items-center gap-4 py-8">
          <BaseSvg name="GitHub" :size="18" color="black" />
          <p class="text-center text-sm text-secondary">{{ t('pages.scripts.marketplace.loginRequired') }}</p>
          <CustomButton
            type="primary"
            :icon="null"
            :text="t('pages.scripts.marketplace.loginWithGitHub')"
            @click="handleGitHubLogin"
          >
            <template #icon>
              <BaseSvg name="GitHub" :size="18" color="black" />
            </template>
          </CustomButton>
        </div>
        <template v-else>
          <SettingCard class="w-full">
            <CustomInput
              v-model="shareMetadata.name"
              :title="t('pages.scripts.marketplace.scriptName')"
              :placeholder="t('pages.scripts.marketplace.scriptName')"
            />
          </SettingCard>
          <SettingCard class="w-full">
            <CustomInput
              v-model="shareMetadata.author"
              :title="t('pages.scripts.marketplace.scriptAuthor')"
              :placeholder="t('pages.scripts.marketplace.scriptAuthor')"
            />
          </SettingCard>
          <SettingCard class="w-full">
            <CustomInput
              v-model="shareMetadata.description"
              :title="t('pages.scripts.marketplace.scriptDescription')"
              :placeholder="t('pages.scripts.marketplace.scriptDescription')"
            />
          </SettingCard>
          <SettingCard class="w-full">
            <CustomInput
              v-model="shareMetadata.version"
              :title="t('pages.scripts.marketplace.scriptVersion')"
              :placeholder="t('pages.scripts.marketplace.scriptVersion')"
            />
          </SettingCard>
        </template>
      </div>
      <template #footer>
        <CustomButton type="secondary" :text="t('common.cancel')" @click="shareDialogVisible = false" />
        <CustomButton
          v-if="githubAuth.isAuthenticated"
          type="primary"
          :text="sharingScript ? t('pages.scripts.marketplace.sharing') : t('pages.scripts.marketplace.share')"
          :disabled="sharingScript"
          @click="handleShareScript"
        />
      </template>
    </CustomModal>

    <CustomModal
      v-if="deviceFlowDialogVisible"
      v-model:visible="deviceFlowDialogVisible"
      :title="t('pages.scripts.marketplace.loginWithGitHub')"
      width="500px"
      height="auto"
    >
      <div class="flex flex-col items-center gap-6 p-6">
        <BaseSvg name="GitHub" :size="18" color="black" />
        <p class="text-center text-secondary">
          {{ t('pages.scripts.marketplace.deviceFlowInstructions') }}
        </p>
        <div class="flex flex-col items-center gap-2">
          <span class="text-sm text-secondary">{{ t('pages.scripts.marketplace.yourCode') }}</span>
          <div class="flex items-center gap-2">
            <code class="rounded-lg bg-bg-tertiary px-6 py-3 text-2xl font-bold tracking-widest text-accent">
              {{ deviceFlowState.userCode }}
            </code>
            <button
              class="rounded-lg bg-bg-secondary p-2 transition-colors hover:bg-bg-tertiary"
              :title="t('pages.scripts.marketplace.copyCode')"
              @click="copyUserCode"
            >
              <CheckIcon v-if="false" :size="20" class="text-success" />
              <svg
                v-else
                xmlns="http://www.w3.org/2000/svg"
                width="20"
                height="20"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                class="text-secondary"
              >
                <rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
                <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
              </svg>
            </button>
          </div>
        </div>
        <div class="flex items-center gap-2">
          <div class="h-5 w-5 animate-spin rounded-full border-2 border-border border-t-accent" />
          <span class="text-sm text-secondary">{{ t('pages.scripts.marketplace.waitingForAuth') }}</span>
        </div>
      </div>
      <template #footer>
        <CustomButton type="secondary" :text="t('common.cancel')" @click="cancelDeviceFlow" />
      </template>
    </CustomModal>
  </div>
</template>

<script lang="ts" setup>
import {
  CheckCircle2,
  CheckIcon,
  Clock,
  DownloadIcon,
  Edit2Icon,
  ExternalLinkIcon,
  FileCode,
  FolderOpen,
  LogOutIcon,
  PackageIcon,
  Pencil,
  Play,
  Plus,
  RefreshCwIcon,
  SearchIcon,
  Share2Icon,
  StoreIcon,
  Trash2,
  UserIcon,
  XCircleIcon,
  XIcon,
} from '@lucide/vue'
import dayjs from 'dayjs'
import { computed, onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'

import BaseSvg from '@/assets/svg/BaseSvg.vue'
import CustomButton from '@/components/common/CustomButton.vue'
import CustomInput from '@/components/common/CustomInput.vue'
import CustomModal from '@/components/common/CustomModal.vue'
import MultiSelect from '@/components/common/MultiSelect.vue'
import SettingCard from '@/components/common/SettingCard.vue'
import SingleSelect from '@/components/common/SingleSelect.vue'
import Editor from '@/components/Editor.vue'
import useConfirm from '@/hooks/useConfirm'
import useMessage from '@/hooks/useMessage'
import { getRawData } from '@/utils/common'
import { configPaths } from '@/utils/configPaths'
import { getConfig, saveConfig } from '@/utils/dataSender'
import { II18nLanguage, IRPCActionType } from '@/utils/enum'
import { defaultScriptTemplate, defaultScriptTemplateEn } from '@/utils/static'

const { t } = useI18n()
const message = useMessage()
const { confirm } = useConfirm()
const scriptsMap = ref<Record<string, any>>({})
const choosedCat = ref<string[]>([])
const scriptsList = ref<IStringKeyMap[]>([])
const editorVisible = ref(false)
const editorContent = ref('')
const editingScriptName = ref<string[]>([])
const newScriptNameVisible = ref(false)
const newScriptName = ref('')
const newScriptCategory = ref('manualTrigger')

// Marketplace related state
interface IScriptMeta {
  name: string
  author: string
  description: string
  version: string
  fileName: string
  category: string
  content: string | null
  downloadUrl: string
}

interface IGitHubAuth {
  isAuthenticated: boolean
  username: string | null
}

interface IDeviceFlowState {
  isActive: boolean
  userCode: string | null
  verificationUri: string | null
  expiresAt: number | null
  pollingInterval: NodeJS.Timeout | null
}

const marketplaceVisible = ref(false)
const marketplaceLoading = ref(false)
const marketplaceError = ref(false)
const marketplaceScripts = ref<IScriptMeta[]>([])
const marketplaceSearch = ref('')
const marketplaceScriptContent = ref('')
const marketplaceCategoryFilter = ref<string[]>([])
const downloadingScripts = ref<Set<string>>(new Set())
const githubAuth = ref<IGitHubAuth>({ isAuthenticated: false, username: null })

// Device Flow state
const deviceFlowState = ref<IDeviceFlowState>({
  isActive: false,
  userCode: null,
  verificationUri: null,
  expiresAt: null,
  pollingInterval: null,
})
const deviceFlowDialogVisible = ref(false)
const scriptContentOfMarketplaceVisible = ref(false)
const shareDialogVisible = ref(false)
const sharingScript = ref(false)
const scriptToShare = ref<IStringKeyMap | null>(null)
const shareMetadata = ref({
  name: '',
  author: '',
  description: '',
  version: '1.0.0',
})

const filteredMarketplaceScripts = computed(() => {
  let scripts = marketplaceScripts.value

  if (marketplaceSearch.value) {
    const search = marketplaceSearch.value.toLowerCase()
    scripts = scripts.filter(
      s =>
        s.name.toLowerCase().includes(search) ||
        s.description.toLowerCase().includes(search) ||
        s.author.toLowerCase().includes(search),
    )
  }

  if (marketplaceCategoryFilter.value.length > 0) {
    scripts = scripts.filter(s => marketplaceCategoryFilter.value.includes(s.category))
  }

  return scripts
})

const supportedScriptCategories = [
  { type: 'onSoftwareOpen', name: t('pages.scripts.scriptsTypes.onSoftwareOpen') },
  { type: 'onSoftwareClose', name: t('pages.scripts.scriptsTypes.onSoftwareClose') },
  { type: 'preProcess', name: t('pages.scripts.scriptsTypes.preProcess') },
  { type: 'beforeTransform', name: t('pages.scripts.scriptsTypes.beforeTransform') },
  { type: 'transform', name: t('pages.scripts.scriptsTypes.transform') },
  { type: 'beforeUpload', name: t('pages.scripts.scriptsTypes.beforeUpload') },
  { type: 'upload', name: t('pages.scripts.scriptsTypes.upload') },
  { type: 'afterUpload', name: t('pages.scripts.scriptsTypes.afterUpload') },
  { type: 'onUploadSuccess', name: t('pages.scripts.scriptsTypes.onUploadSuccess') },
  { type: 'onGalleryRemove', name: t('pages.scripts.scriptsTypes.onGalleryRemove') },
  { type: 'manualTrigger', name: t('pages.scripts.scriptsTypes.manualTrigger') },
  { type: 'uploader.advancedplist', name: t('pages.scripts.scriptsTypes.uploader.advancedplist') },
]

const existingPathsSet = computed(() => {
  return new Set(scriptsList.value.map(item => item.filePath.join('/')))
})

watch(scriptsMap, async () => {
  await refreshList()
})

watch(choosedCat, async () => {
  await refreshList()
})

async function refreshList() {
  const result: string[][] = []
  const keysToCheck = choosedCat.value.length > 0 ? choosedCat.value : supportedScriptCategories.map(cat => cat.type)
  for (const key of keysToCheck) {
    if (key.includes('.')) {
      const parts = key.split('.')
      const value = scriptsMap.value[parts[0]] ? scriptsMap.value[parts[0]][parts[1]] : undefined
      if (value) {
        Object.entries(value).forEach(([valueKey, item]: [string, any]) => {
          if (item === null) {
            result.push([parts[0], parts[1], valueKey])
          }
        })
      }
    } else {
      const value = scriptsMap.value[key]
      if (value) {
        Object.entries(value).forEach(([valueKey, item]: [string, any]) => {
          if (item === null) {
            result.push([key, valueKey])
          }
        })
      }
    }
  }
  const fileStats =
    (await window.electron.triggerRPC<IObj[]>(IRPCActionType.GET_FILES_STAT, getRawData(result), 'scripts')) || []
  const disabledList = ((await getConfig(configPaths.scripts.disabledList)) as string[] | undefined) || []
  fileStats.forEach(file => {
    const fullPath = file.filePath.join('/')
    file.enabled = !disabledList.includes(fullPath)
  })
  scriptsList.value = fileStats
}

async function getScriptsMap() {
  scriptsMap.value =
    (await window.electron.triggerRPC<Record<string, any>>(IRPCActionType.LIST_SCRIPTS_FILES, [])) || {}
}

function formatDate(timestamp: number) {
  const date = dayjs(timestamp)
  return date.format('YYYY/MM/DD HH:mm:ss')
}

async function getTemplate() {
  const lang = (await getConfig(configPaths.settings.language)) || II18nLanguage.ZH_CN
  if (lang === II18nLanguage.ZH_CN || lang === II18nLanguage.ZH_TW) {
    return defaultScriptTemplate
  } else {
    return defaultScriptTemplateEn
  }
}

async function openEditPage(filePath: string[], mode: 'edit' | 'new' = 'edit') {
  editingScriptName.value = filePath
  if (mode === 'edit') {
    const content =
      (await window.electron.triggerRPC<string>(IRPCActionType.READ_SCRIPTS_FILE, getRawData(filePath))) || ''
    editorContent.value = content
  } else {
    editorContent.value = await getTemplate()
  }
  editorVisible.value = true
}

async function saveEditorContent() {
  const content = editorContent.value.trim()
  try {
    window.electron.sendRPC(IRPCActionType.WRITE_SCRIPT_FILE, getRawData(editingScriptName.value), content)
    message.success(t('pages.settings.advanced.saveFileSuccess'))
    await getScriptsMap()
  } catch (error) {
    console.error('Failed to save file:', error)
    message.error(t('pages.settings.advanced.saveFileFailed'))
  }
  editorVisible.value = false
}

async function deleteConfig(scriptPath: string[]) {
  const result = await confirm({
    title: t('pages.scripts.deleteScriptTitle'),
    message: t('pages.scripts.deleteScriptConfirm', { name: scriptPath[scriptPath.length - 1] }),
    type: 'warning',
    confirmButtonText: t('common.confirm'),
    cancelButtonText: t('common.cancel'),
    center: true,
  })
  if (!result) return
  try {
    window.electron.sendRPC(IRPCActionType.DELETE_SCRIPTS_FILE, getRawData(scriptPath))
    message.success(t('pages.scripts.deleteSuccess'))
    await getScriptsMap()
  } catch (error) {
    console.error('Failed to delete script file:', error)
    message.error(t('pages.scripts.deleteFailed'))
  }
}

function handleOpenScriptFolder() {
  window.electron.sendRPC(IRPCActionType.PICLIST_OPEN_DIRECTORY, 'scripts', true)
}

function openNewScriptsNameDialog() {
  newScriptName.value = ''
  newScriptNameVisible.value = true
}

async function runScript(scriptPath: string[]) {
  const result = await window.electron.triggerRPC(IRPCActionType.RUN_SCRIPT_FILE, getRawData(scriptPath))
  if (result instanceof Error) {
    const errorMessage = result.message || 'Unknown error'
    message.error(`${t('pages.scripts.runScriptFailed', { errorMessage })}`)
  } else {
    message.success(t('pages.scripts.runScriptSuccess'))
  }
}

function checkDup(fullPath: string[]) {
  return existingPathsSet.value.has(fullPath.join('/'))
}

function handleNewScriptNameConfirm() {
  let trimmedName = newScriptName.value.trim()
  trimmedName = trimmedName.endsWith('.js') ? trimmedName : `${trimmedName}.js`
  if (!trimmedName) {
    message.error(t('pages.scripts.pleaseEnterScriptName'))
    return
  }
  const scriptPath = newScriptCategory.value.includes('.')
    ? [...newScriptCategory.value.split('.'), trimmedName]
    : [newScriptCategory.value, trimmedName]
  if (checkDup(scriptPath)) {
    message.error(t('pages.scripts.duplicateScriptNameError'))
    return
  }
  newScriptNameVisible.value = false
  openEditPage(scriptPath, 'new')
}

async function toggleScript(scriptPath: string[]) {
  const disabledList = ((await getConfig(configPaths.scripts.disabledList)) as string[] | undefined) || []
  const fullPath = scriptPath.join('/')
  if (disabledList.includes(fullPath)) {
    const index = disabledList.indexOf(fullPath)
    if (index > -1) {
      disabledList.splice(index, 1)
    }
  } else {
    disabledList.push(fullPath)
  }
  saveConfig(configPaths.scripts.disabledList, disabledList)
  await getScriptsMap()
}

function openScriptDetails(content: string) {
  marketplaceScriptContent.value = content
  scriptContentOfMarketplaceVisible.value = true
}

async function openMarketplace() {
  marketplaceVisible.value = true
  await checkGitHubAuth()
  await fetchMarketplaceScripts()
}

async function fetchMarketplaceScripts() {
  marketplaceLoading.value = true
  marketplaceError.value = false

  try {
    const scripts = await window.electron.triggerRPC<IScriptMeta[]>(IRPCActionType.SCRIPT_MARKETPLACE_FETCH_LIST)
    marketplaceScripts.value = scripts || []
  } catch (error) {
    console.error('Failed to fetch marketplace scripts:', error)
    marketplaceError.value = true
  } finally {
    marketplaceLoading.value = false
  }
}

function isScriptDownloaded(script: IScriptMeta): boolean {
  const categoryPath = script.category.replace(/\./g, '/')
  const scriptPath = `${categoryPath}/${script.fileName}`
  const isDownloaded = existingPathsSet.value.has(scriptPath)
  return isDownloaded
}

async function downloadMarketplaceScript(script: IScriptMeta) {
  downloadingScripts.value.add(script.downloadUrl)

  try {
    const result = await window.electron.triggerRPC<boolean>(
      IRPCActionType.SCRIPT_MARKETPLACE_DOWNLOAD,
      getRawData(script),
    )
    if (result) {
      message.success(t('pages.scripts.marketplace.downloadSuccess'))
      await getScriptsMap()
    } else {
      message.error(t('pages.scripts.marketplace.downloadFailed'))
    }
  } catch (error) {
    console.error('Failed to download script:', error)
    message.error(t('pages.scripts.marketplace.downloadFailed'))
  } finally {
    downloadingScripts.value.delete(script.downloadUrl)
  }
}

async function checkGitHubAuth() {
  try {
    const auth = await window.electron.triggerRPC<IGitHubAuth>(IRPCActionType.SCRIPT_MARKETPLACE_CHECK_GITHUB_AUTH)
    if (auth) {
      githubAuth.value = auth
    }
  } catch (error) {
    console.error('Failed to check GitHub auth:', error)
  }
}

async function handleGitHubLogin() {
  try {
    const result = await window.electron.triggerRPC<{
      userCode: string
      verificationUri: string
      expiresIn: number
    } | null>(IRPCActionType.SCRIPT_MARKETPLACE_GITHUB_LOGIN)

    if (result) {
      deviceFlowState.value = {
        isActive: true,
        userCode: result.userCode,
        verificationUri: result.verificationUri,
        expiresAt: Date.now() + result.expiresIn * 1000,
        pollingInterval: null,
      }
      deviceFlowDialogVisible.value = true

      startDeviceFlowPolling()
    } else {
      message.error(t('pages.scripts.marketplace.loginFailed'))
    }
  } catch (error) {
    console.error('Failed to initiate GitHub login:', error)
    message.error(t('pages.scripts.marketplace.loginFailed'))
  }
}

function startDeviceFlowPolling() {
  if (deviceFlowState.value.pollingInterval) {
    clearInterval(deviceFlowState.value.pollingInterval)
  }

  const poll = async () => {
    if (deviceFlowState.value.expiresAt && Date.now() > deviceFlowState.value.expiresAt) {
      stopDeviceFlowPolling()
      message.error(t('pages.scripts.marketplace.deviceCodeExpired'))
      deviceFlowDialogVisible.value = false
      return
    }

    try {
      const result = await window.electron.triggerRPC<{
        success: boolean
        username?: string
        error?: string
        nextInterval?: number
      }>(IRPCActionType.SCRIPT_MARKETPLACE_GITHUB_POLL)

      if (result?.success) {
        stopDeviceFlowPolling()
        githubAuth.value = { isAuthenticated: true, username: result.username || null }
        deviceFlowDialogVisible.value = false
        message.success(t('pages.scripts.marketplace.loginSuccess'))
        return
      }

      if (result?.error === 'authorization_pending' || result?.error === 'slow_down') {
        const delay = (result.nextInterval || 5) * 1000
        deviceFlowState.value.pollingInterval = setTimeout(poll, delay)
      } else {
        stopDeviceFlowPolling()
        deviceFlowDialogVisible.value = false
        message.error(`${t('pages.scripts.marketplace.loginFailed')}: ${result?.error}`)
      }
    } catch (error) {
      console.error('Failed to poll device flow:', error)
      deviceFlowState.value.pollingInterval = setTimeout(poll, 5000)
    }
  }

  poll()
}

function stopDeviceFlowPolling() {
  if (deviceFlowState.value.pollingInterval) {
    clearInterval(deviceFlowState.value.pollingInterval)
    deviceFlowState.value.pollingInterval = null
  }
  deviceFlowState.value.isActive = false
}

async function cancelDeviceFlow() {
  stopDeviceFlowPolling()
  await window.electron.triggerRPC(IRPCActionType.SCRIPT_MARKETPLACE_GITHUB_CANCEL)
  deviceFlowDialogVisible.value = false
}

function copyUserCode() {
  if (deviceFlowState.value.userCode) {
    window.electron.clipboard.writeText(deviceFlowState.value.userCode)
    message.success(t('pages.scripts.marketplace.codeCopied'))
  }
}

async function handleGitHubLogout() {
  try {
    await window.electron.triggerRPC(IRPCActionType.SCRIPT_MARKETPLACE_GITHUB_LOGOUT)
    githubAuth.value = { isAuthenticated: false, username: null }
    message.success(t('pages.scripts.marketplace.logoutSuccess'))
  } catch (error) {
    console.error('Failed to logout:', error)
  }
}

function openMarketplaceRepo() {
  window.electron.sendRPC(IRPCActionType.OPEN_URL, 'https://github.com/Kuingsmile/piclist-ScriptsHub')
}

function openShareDialog(script: IStringKeyMap) {
  scriptToShare.value = script
  shareMetadata.value = {
    name: script.fileName.replace('.js', ''),
    author: githubAuth.value.username || '',
    description: '',
    version: '1.0.0',
  }
  shareDialogVisible.value = true
}

async function handleShareScript() {
  if (!scriptToShare.value) return

  if (
    !shareMetadata.value.name ||
    !shareMetadata.value.author ||
    !shareMetadata.value.description ||
    !shareMetadata.value.version
  ) {
    message.error(t('pages.scripts.marketplace.metadataRequired'))
    return
  }

  sharingScript.value = true

  try {
    const result = await window.electron.triggerRPC<{ success: boolean; prUrl?: string; error?: string }>(
      IRPCActionType.SCRIPT_MARKETPLACE_SHARE,
      getRawData(scriptToShare.value.filePath),
      getRawData(shareMetadata.value),
    )

    if (result?.success) {
      message.success(t('pages.scripts.marketplace.shareSuccess'))
      shareDialogVisible.value = false
      if (result.prUrl) {
        window.electron.sendRPC(IRPCActionType.OPEN_URL, result.prUrl)
      }
    } else {
      message.error(`${t('pages.scripts.marketplace.shareFailed')}: ${result?.error || 'Unknown error'}`)
    }
  } catch (error) {
    console.error('Failed to share script:', error)
    message.error(t('pages.scripts.marketplace.shareFailed'))
  } finally {
    sharingScript.value = false
  }
}

onBeforeMount(async () => {
  getScriptsMap()
  await checkGitHubAuth()
})

onBeforeUnmount(() => {
  stopDeviceFlowPolling()
})
</script>

<script lang="ts">
export default {
  name: 'ScriptPage',
}
</script>

<style scoped>
@import 'tailwindcss' reference;
@import '../assets/css/theme.css' reference;
@import '../assets/css/utilities.css' reference;

.action-btn {
  @apply flex h-[30px] w-[30px] cursor-pointer items-center justify-center rounded-md border border-accent/20 text-secondary transition-all duration-fast ease-standard hover:scale-105 hover:bg-accent/30 hover:text-white disabled:cursor-not-allowed disabled:opacity-50 hover:not-disabled:[.danger]:border-danger hover:not-disabled:[.danger]:bg-danger;
}
</style>
