import { defineStore } from 'pinia'
import { API } from '@/Api'
import { reactive, ref, nextTick, computed } from 'vue'
import { debounce, isEmpty, pick } from 'lodash'
import { useIndexingStore, type JobBlob } from './indexingStore'
import { isIndexing } from '@/constants/indexing'
import { DEFAULT_AGENT_NAME } from '@/constants/conversation'
import { WT1iser } from '@/lib/wt1'
import { toast } from 'vue-sonner'
export type DocObj = {
  name: string
  isFromServer?: true
  active?: boolean
  deletePending?: boolean
  size?: number
}

export type DocEntry = File | DocObj
type Draft = {
  id: string
  name: string
  description: string
  systemPrompt: string
  kbDescription: string
  tools: string[]
  toolDetails?: { id: string; name: string; description: string }[]
  llmid: string
  embeddingLlmId: string
  documents: DocEntry[]
  sample_questions?: string[]
  indexing?: { status: string; jobId?: string; updatedAt?: string }
  status?: 'draft' | 'active'
  published_version?: {
    name: string
    description: string
    system_prompt: string
    kb_description: string
    tools: string[]
    llmid: string
    embedding_llm_id: string
    documents: any[]
    sample_questions?: string[]
  }
  published_at?: string
  publishing_status?: 'idle' | 'publishing' | 'published' | 'failed'
  hasUnpublishedChanges?: boolean
  project_key?: string
}

const SNAPSHOT_KEYS: (keyof Draft)[] = [
  'name',
  'description',
  'systemPrompt',
  'kbDescription',
  'tools',
  'llmid',
  'embeddingLlmId',
  'sample_questions',
  'project_key',
  //'documents',
  //'indexing',
  'status',
]
export const snapshot = (d: Draft) => pick(d, SNAPSHOT_KEYS)

export const useAgentStore = defineStore('agent', () => {
  const hydrating = ref(false)
  const isSaving = ref(false)
  const saveFailed = ref(false)

  const draft = reactive<Draft>({
    id: '',
    name: '',
    description: '',
    systemPrompt: '',
    kbDescription: '',
    tools: [],
    toolDetails: [],
    documents: [],
    llmid: '',
    embeddingLlmId: '',
    sample_questions: [],
    project_key: '',
    indexing: { status: '' } as JobBlob,
    status: 'draft',
  })

  const lastSavedJson = ref<string>('')
  const noProjectCreated = computed(() => !draft.id || draft.id.indexOf('ac_user_agent_') < 0)

  function refreshSnapshot() {
    lastSavedJson.value = JSON.stringify(snapshot(draft))
  }

  const hasName = () => !!draft.name.trim()
  const hasNameAndDesc = () => hasName() && !!draft.description.trim()

  // TODO: should be named hasUnpublishedChanges ?
  function hasUnsavedChanges(): boolean {
    if (draft.published_version) {
      const fieldMappings = [
        { draft: 'name', published: 'name' },
        { draft: 'description', published: 'description' },
        { draft: 'systemPrompt', published: 'system_prompt' },
        { draft: 'kbDescription', published: 'kb_description' },
        { draft: 'llmid', published: 'llmid' },
      ]

      // Check each field against published version
      for (const mapping of fieldMappings) {
        const draftValue = draft[mapping.draft as keyof Draft]
        const publishedValue =
          draft.published_version[mapping.published as keyof typeof draft.published_version]
        if (draftValue !== publishedValue) {
          return true
        }
      }

      // Check sample_questions array (order-insensitive)
      const currentQuestions = [...(draft.sample_questions || [])].sort()
      const publishedQuestions = [...(draft.published_version.sample_questions || [])].sort()
      if (JSON.stringify(currentQuestions) !== JSON.stringify(publishedQuestions)) {
        return true
      }

      // Check tools array (order-insensitive comparison)
      const currentTools = [...(draft.tools || [])].sort()
      const publishedTools = [...(draft.published_version.tools || [])].sort()
      if (JSON.stringify(currentTools) !== JSON.stringify(publishedTools)) {
        return true
      }

      // Check documents (comparing only active documents, order-insensitive)
      const normalizeDoc = (d: any) => {
        if (typeof d === 'string') return d
        return d.name || ''
      }

      const currentDocs = (draft.documents || [])
        .filter((d) => {
          if (typeof d === 'object' && 'active' in d && 'deletePending' in d) {
            return d.active && !d.deletePending
          }
          return true
        })
        .map(normalizeDoc)
        .sort()

      const publishedDocs = (draft.published_version.documents || [])
        .filter((d) => {
          if (typeof d === 'object' && 'active' in d && 'deletePending' in d) {
            return d.active && !d.deletePending
          }
          return true
        })
        .map(normalizeDoc)
        .sort()

      if (JSON.stringify(currentDocs) !== JSON.stringify(publishedDocs)) {
        return true
      }

      return false
    }

    // For new agents, compare against empty state
    const currentSnapshot = JSON.stringify(snapshot(draft))
    return currentSnapshot !== lastSavedJson.value
  }

  function initDraft() {
    autosave.flush?.()
    autosave.cancel?.()
    saveFailed.value = false
    hydrating.value = true

    draft.id = Date.now().toString()
    draft.name = ''
    draft.description = ''
    draft.systemPrompt = ''
    draft.kbDescription = ''
    draft.tools = []
    draft.documents = []
    draft.sample_questions = []
    draft.indexing = { status: '' }
    draft.status = 'draft'
    draft.llmid = ''
    draft.embeddingLlmId = ''
    draft.published_version = undefined
    draft.published_at = undefined
    draft.publishing_status = 'idle'
    draft.hasUnpublishedChanges = false
    draft.project_key = ''

    refreshSnapshot()
    nextTick(() => {
      hydrating.value = false
    })
  }
  async function doSave(force = false) {
    const changed = JSON.stringify(snapshot(draft)) !== lastSavedJson.value
    if (!force && !changed) return

    if (!hasName()) return

    const form = new FormData()
    form.append('id', draft.id)
    form.append('name', draft.name)
    form.append('description', draft.description)
    form.append('systemPrompt', draft.systemPrompt)
    form.append('kb_description', draft.kbDescription)
    form.append('status', draft.status ?? 'draft')
    draft.status = 'draft'

    if (draft.llmid) {
      form.append('llmid', draft.llmid)
    }
    // form.append('embedding_llm_id', draft.embeddingLlmId)
    if (draft.tools.length === 0) form.append('tools', '')
    else draft.tools.forEach((t) => form.append('tools', t))

    // Add sample questions
    if (draft.sample_questions) {
      const filtered = draft.sample_questions.filter((q) => q.trim() !== '')
      if (filtered.length === 0) form.append('sample_questions', '')
      else filtered.forEach((q) => form.append('sample_questions', q))
    }

    draft.documents.forEach((d: any) =>
      form.append('documents', typeof d === 'string' ? d : d.name)
    )

    // Capture snapshot before save to detect if changes occur during the API call
    const preSaveSnapshot = JSON.stringify(snapshot(draft))
    WT1iser.saveAgent(draft)
    try {
      isSaving.value = true
      const res = await API.createAgent(form)
      draft.project_key = res.data.id.indexOf('ac_user_agent_') === 0 ? res.data.id : ''
      draft.id = res.data.id
      saveFailed.value = false
      window.dispatchEvent(
        new CustomEvent('agent-saved', {
          detail: {
            id: draft.id,
            name: draft.name,
            status: draft.status,
            hasUnpublishedChanges: hasUnsavedChanges(),
            indexing: draft.indexing,
            project_key: draft.project_key,
          },
        })
      )
      // Race condition fix
      // Check if state changed during save (race condition)
      // If unchanged, update last saved snapshot
      // If changed, skip update and trigger another save to persist interim changes
      if (JSON.stringify(snapshot(draft)) === preSaveSnapshot) {
        refreshSnapshot()
      } else {
        void doSave()
      }
    } catch (e) {
      saveFailed.value = true
      console.error('Error saving agent draft', e)
      toast.error('Error saving agent. Please try again.')
    } finally {
      isSaving.value = false
    }
  }

  const autosave = debounce((payload?: Partial<Draft>, shallow = false) => {
    if (payload) Object.assign(draft, payload)
    if (hydrating.value) return
    if (shallow) return
    void doSave()
  }, 1000)

  function flushPendingAutosave() {
    autosave.flush?.()
    autosave.cancel?.()
  }

  async function createAgent() {
    if (!hasNameAndDesc()) {
      return Promise.reject(new Error('Name and description are required'))
    }
    const form = new FormData()
    form.append('id', draft.id)
    form.append('name', draft.name)
    form.append('description', draft.description)
    form.append('systemPrompt', draft.systemPrompt)
    form.append('kb_description', draft.kbDescription)
    form.append('status', 'draft')
    draft.status = 'draft'
    form.append('llmid', draft.llmid || '')
    // form.append('embedding_llm_id', draft.embeddingLlmId || '')

    if (draft.tools.length === 0) form.append('tools', '')
    else draft.tools.forEach((t) => form.append('tools', t))

    // Add sample questions
    if (draft.sample_questions) {
      draft.sample_questions.forEach((q) => form.append('sample_questions', q))
    }

    try {
      isSaving.value = true
      const res = await API.createAgent(form)
      window.dispatchEvent(
        new CustomEvent('agent-saved', {
          detail: {
            id: draft.id,
            name: draft.name,
            status: 'draft',
            hasUnpublishedChanges: hasUnsavedChanges(),
            project_key: draft.project_key,
          },
        })
      )
      refreshSnapshot()
      return res
    } finally {
      isSaving.value = false
    }
  }

  async function publishAgent() {
    if (!hasNameAndDesc()) {
      return Promise.reject(new Error('Name and description are required'))
    }

    try {
      await createAgent()

      const res = await API.publishAgent(draft.id)
      return res
    } catch (err) {
      throw err
    }
  }

  async function loadDraft(agentId: string) {
    hydrating.value = true
    saveFailed.value = false
    try {
      const res = await API.getAgent(agentId)
      const a = res.data

      draft.id = a.id || ''
      draft.name = a.name || ''
      draft.description = a.description ?? ''
      draft.systemPrompt = a.system_prompt || ''
      draft.kbDescription = a.kb_description || ''
      if (Array.isArray(a.tools) && a.tools.length && typeof a.tools[0] !== 'string') {
        draft.tools = a.tools.map((t: any) => t.id)
      } else {
        draft.tools = a.tools ?? []
      }
      draft.toolDetails = a.toolDetails ?? []
      draft.sample_questions = a.sample_questions ?? []
      draft.documents = (a.documents || []).map((d: any) =>
        typeof d === 'string' ? { name: d, active: true } : d
      )
      draft.indexing = a.indexing ?? { status: '' }
      draft.status = a.status || 'draft'
      draft.llmid = a.llmid || ''
      draft.embeddingLlmId = a.embedding_llm_id || ''
      draft.published_version = a.published_version
      draft.published_at = a.published_at
      draft.publishing_status = a.publishing_status || 'idle'
      draft.hasUnpublishedChanges = a.hasUnpublishedChanges || false
      draft.project_key = a.id.indexOf('ac_user_agent_') === 0 ? a.id : ''

      const idxStore = useIndexingStore()
      if (isIndexing(draft.indexing?.status)) {
        idxStore.track(draft.id, draft.indexing!, draft.name)
      }
      refreshSnapshot()
    } finally {
      await nextTick()
      hydrating.value = false
    }
  }
  async function generateAgentConfig() {
    hydrating.value = true
    try {
      const payload = {
        agent_name: draft.name,
        description: draft.description,
        system_prompt: draft.systemPrompt,
      }
      const res = await API.getAgentConfig(payload)
      if (res.data) {
        draft.description =
          isEmpty(draft.description.trim()) && res.data.description
            ? res.data.description
            : draft.description
        draft.systemPrompt =
          isEmpty(draft.systemPrompt.trim()) && res.data.system_prompt
            ? res.data.system_prompt
            : draft.systemPrompt
        draft.sample_questions =
          isEmpty(draft.sample_questions?.filter((el) => !isEmpty(el))) &&
          res.data.example_long_queries
            ? res.data.example_long_queries
            : draft.sample_questions
        draft.name =
          (isEmpty(draft.name.trim()) || draft.name === DEFAULT_AGENT_NAME) && res.data.agent_name
            ? res.data.agent_name
            : draft.name
        doSave()
      }
    } catch (e) {
      console.error('Error generating agent config', e)
    } finally {
      await nextTick()
      hydrating.value = false
    }
  }

  return {
    draft,
    hydrating,
    isSaving,
    saveFailed,
    noProjectCreated,
    initDraft,
    autosave,
    doSave,
    flushPendingAutosave,
    createAgent,
    loadDraft,
    publishAgent,
    refreshSnapshot,
    hasUnsavedChanges,
    lastSavedJson,
    generateAgentConfig,
  }
})
