import { ref, reactive, computed, onMounted } from 'vue'

const { toast, confirm } = pragma.ui.helpers
const { Icon, JsonEditor } = pragma.ui.components
const {
  getContentTypes,
  getSourceContent,
  updateContent,
  publishContent,
  checkDisparity,
  downloadContentArchive,
  commitContentToGithub,
  isGithubServiceConfigured
} = pragma.ext.operator.api.content

const ContentEditor = {
  name: 'ContentEditor',
  components: { Icon, JsonEditor },
  setup() {
    const contentTypes = ref([])
    const selectedContentType = ref(null)
    const sourceFiles = ref([])
    const selectedFile = ref(null)
    const editorContent = ref('')
    const originalContent = ref('')
    const disparities = ref([])
    const loading = reactive({
      types: false,
      files: false,
      saving: false,
      publishing: false,
      disparity: false,
      downloading: false,
      committing: false
    })
    const githubModal = reactive({
      open: false,
      title: '',
      body: ''
    })
    const searchQuery = ref('')
    const validationErrors = ref([])
    const downloadLockedAfterPublish = ref(false)
    const POST_PUBLISH_DOWNLOAD_LOCKOUT_MS = 16_000
    const githubServiceAvailable = ref(false)

    const hasChanges = computed(() => editorContent.value !== originalContent.value)
    const hasValidationErrors = computed(() => validationErrors.value.length > 0)

    const filteredContentTypes = computed(() => {
      if (!searchQuery.value) return contentTypes.value
      const q = searchQuery.value.toLowerCase()
      return contentTypes.value.filter(t => t.toLowerCase().includes(q))
    })

    const hasDisparities = computed(() =>
      disparities.value.some(d => !d.matches)
    )

    const disparitySummary = computed(() => {
      const mismatched = disparities.value.filter(d => !d.matches)
      if (mismatched.length === 0) return ''
      const types = [...new Set(mismatched.map(d => d.contentType))]
      return `${types.length} content type(s) have database overrides that differ from deployed content: ${types.join(', ')}`
    })

    const loadContentTypes = async () => {
      loading.types = true
      try {
        contentTypes.value = await getContentTypes()
      } catch (e) {
        toast.error('Failed to load content types')
      } finally {
        loading.types = false
      }
    }

    const loadDisparities = async () => {
      loading.disparity = true
      try {
        disparities.value = await checkDisparity()
      } catch (e) {
        console.error('Failed to check content disparity', e)
      } finally {
        loading.disparity = false
      }
    }

    const selectContentType = async (contentType) => {
      if (hasChanges.value) {
        confirm.danger({
          title: 'Unsaved Changes',
          content: 'You have unsaved changes. Discard them?',
          okText: 'Discard',
          onOk: () => doSelectContentType(contentType)
        })
      } else {
        await doSelectContentType(contentType)
      }
    }

    const doSelectContentType = async (contentType) => {
      selectedContentType.value = contentType
      selectedFile.value = null
      editorContent.value = ''
      originalContent.value = ''
      validationErrors.value = []
      loading.files = true
      try {
        sourceFiles.value = await getSourceContent(contentType)
        if (sourceFiles.value.length > 0) {
          selectFile(sourceFiles.value[0])
        }
      } catch (e) {
        toast.error(`Failed to load source files for ${contentType}`)
        sourceFiles.value = []
      } finally {
        loading.files = false
      }
    }

    const selectFile = (file) => {
      selectedFile.value = file
      const formatted = tryFormatJson(file.content)
      editorContent.value = formatted
      originalContent.value = formatted
      validationErrors.value = []
    }

    const tryFormatJson = (jsonString) => {
      try {
        return JSON.stringify(JSON.parse(jsonString), null, 2)
      } catch {
        return jsonString
      }
    }

    const onEditorUpdate = (newValue) => {
      editorContent.value = newValue
    }

    const onValidationChange = (markers) => {
      validationErrors.value = markers.filter(m => m.severity >= 8)
    }

    const saveContent = async () => {
      if (!selectedFile.value || !selectedContentType.value) return
      if (hasValidationErrors.value) {
        toast.error('Cannot save: JSON has validation errors')
        return
      }

      loading.saving = true
      try {
        await updateContent(
          selectedContentType.value,
          selectedFile.value.filename,
          editorContent.value
        )
        originalContent.value = editorContent.value
        toast.success('Content saved')
      } catch (e) {
        toast.error(`Save failed: ${e.message}`, 15)
      } finally {
        loading.saving = false
      }
    }

    const doPublish = async () => {
      loading.publishing = true
      try {
        await publishContent()
        toast.success('Content published successfully')
        downloadLockedAfterPublish.value = true
        setTimeout(() => { downloadLockedAfterPublish.value = false }, POST_PUBLISH_DOWNLOAD_LOCKOUT_MS)
        await loadDisparities()
      } catch (e) {
        toast.error(`Publish failed: ${e.message}`, 15)
      } finally {
        loading.publishing = false
      }
    }

    const handlePublish = () => {
      confirm.danger({
        title: 'Publish Content',
        content: 'This will persist all pending content changes to the database. All nodes will pick up the changes within 15 seconds.',
        okText: 'Publish',
        onOk: doPublish
      })
    }

    const revertChanges = () => {
      editorContent.value = originalContent.value
      validationErrors.value = []
    }

    const handleDownloadArchive = async () => {
      loading.downloading = true
      try {
        const filename = await downloadContentArchive()
        toast.success(`Downloaded ${filename}`)
      } catch (e) {
        toast.error(`Download failed: ${e.message}`, 15)
      } finally {
        loading.downloading = false
      }
    }

    const openCommitDialog = () => {
      githubModal.title = ''
      githubModal.body = ''
      githubModal.open = true
    }

    const submitCommit = async () => {
      loading.committing = true
      try {
        const result = await commitContentToGithub({
          title: githubModal.title,
          body: githubModal.body
        })
        githubModal.open = false
        toast.success(`PR opened: ${result.pullRequestUrl}`, 30)
      } catch (e) {
        toast.error(`Commit failed: ${e.message}`, 30)
      } finally {
        loading.committing = false
      }
    }

    const loadGithubServiceAvailability = async () => {
      try {
        githubServiceAvailable.value = await isGithubServiceConfigured()
      } catch (e) {
        githubServiceAvailable.value = false
      }
    }

    onMounted(async () => {
      await Promise.all([loadContentTypes(), loadDisparities(), loadGithubServiceAvailability()])
    })

    return {
      contentTypes,
      selectedContentType,
      sourceFiles,
      selectedFile,
      editorContent,
      hasChanges,
      disparities,
      loading,
      searchQuery,
      validationErrors,
      hasValidationErrors,
      filteredContentTypes,
      hasDisparities,
      disparitySummary,
      selectContentType,
      selectFile,
      onEditorUpdate,
      onValidationChange,
      saveContent,
      handlePublish,
      handleDownloadArchive,
      openCommitDialog,
      submitCommit,
      githubModal,
      downloadLockedAfterPublish,
      revertChanges,
      githubServiceAvailable
    }
  },
  template: `
    <div class="content-editor">
      <div class="page-toolbar" style="display: flex; align-items: flex-start; gap: 12px; margin-bottom: 16px">
        <a-alert
          v-if="hasDisparities"
          type="warning"
          show-icon
          :message="disparitySummary"
          style="flex: 1"
        />
        <div v-else style="flex: 1"></div>
        <a-tooltip :title="downloadLockedAfterPublish ? 'Waiting for the published changes to propagate to all nodes...' : 'Downloads content sources (you need to push to git to generate a versioned artifact)'">
          <a-button
            size="small"
            @click="handleDownloadArchive"
            :loading="loading.downloading"
            :disabled="downloadLockedAfterPublish"
          >
            Download archive
          </a-button>
        </a-tooltip>
        <a-tooltip title="Publish staged content updates. Takes ~15 seconds to propagate to all nodes.">
          <a-button
            type="primary"
            size="small"
            @click="handlePublish"
            :loading="loading.publishing"
            danger
          >
            Publish
          </a-button>
        </a-tooltip>
        <a-tooltip v-if="githubServiceAvailable" title="Commit current src + package contents to the configured GitHub repository and open a PR.">
          <a-button
            type="primary"
            size="small"
            @click="openCommitDialog"
          >
            Commit to GitHub
          </a-button>
        </a-tooltip>
      </div>

      <a-modal
        v-model:open="githubModal.open"
        title="Commit content to GitHub"
        :confirm-loading="loading.committing"
        :ok-text="loading.committing ? 'Committing...' : 'Open PR'"
        cancel-text="Cancel"
        @ok="submitCommit"
      >
        <p>
          Opens a PR against the configured branch with the current contents of
          <code>content/src</code> and <code>content/package</code> on this node.
        </p>
        <a-input
          v-model:value="githubModal.title"
          placeholder="PR title (defaults to 'Content update <timestamp>')"
          style="margin-bottom: 12px"
        />
        <a-textarea
          v-model:value="githubModal.body"
          placeholder="PR description (optional)"
          :rows="4"
        />
      </a-modal>

      <div class="content-editor-layout">
        <div class="content-sidebar">
          <div class="sidebar-header">
            <h4>Content Types</h4>
            <a-input
              v-model:value="searchQuery"
              placeholder="Search..."
              size="small"
              allow-clear
              style="margin-top: 8px"
            />
          </div>
          <div class="sidebar-list">
            <a-spin v-if="loading.types" style="display: block; text-align: center; padding: 24px" />
            <div v-else-if="filteredContentTypes.length === 0" style="padding: 16px; color: #999; text-align: center">
              No content types found
            </div>
            <div
              v-for="ct in filteredContentTypes"
              :key="ct"
              class="sidebar-item"
              :class="{ active: ct === selectedContentType }"
              @click="selectContentType(ct)"
            >
              {{ ct }}
            </div>
          </div>
        </div>

        <div class="content-main">
          <div v-if="!selectedContentType" class="content-empty-state">
            <Icon name="nullSet" :size="80" />
            <h4>Select a content type</h4>
            <p>Choose a content type from the sidebar to view and edit its source files.</p>
          </div>

          <template v-else>
            <div class="editor-toolbar">
              <div class="file-tabs">
                <a-button
                  v-for="file in sourceFiles"
                  :key="file.filename"
                  :type="file.filename === selectedFile?.filename ? 'primary' : 'default'"
                  size="small"
                  @click="selectFile(file)"
                >
                  {{ file.filename }}
                </a-button>
              </div>
              <div class="editor-actions">
                <a-tag v-if="hasChanges" color="orange">Unsaved changes</a-tag>
                <a-tag v-if="hasValidationErrors" color="red">JSON errors</a-tag>
                <a-button
                  size="small"
                  @click="revertChanges"
                  :disabled="!hasChanges"
                >
                  Revert
                </a-button>
                <a-tooltip title="Prepares changes to be published. No content changes applied until publish step performed.">
                  <a-button
                    type="primary"
                    size="small"
                    @click="saveContent"
                    :loading="loading.saving"
                    :disabled="!hasChanges || hasValidationErrors"
                  >
                    Stage
                  </a-button>
                </a-tooltip>
              </div>
            </div>

            <div class="editor-container">
              <a-spin v-if="loading.files" style="display: block; text-align: center; padding: 48px" />
              <JsonEditor
                v-else-if="selectedFile"
                :rawData="editorContent"
                :readOnly="false"
                :style="'height: calc(100vh - 280px); min-height: 400px'"
                @onUpdate="onEditorUpdate"
                @validationChange="onValidationChange"
              />
            </div>
          </template>
        </div>
      </div>
    </div>
  `
}

export default ContentEditor
