import { Button, Space, Table, Tabs, Typography } from 'antd'
import React, { useMemo, useState } from 'react'
import { useParams } from 'react-router-dom'
import { parse as parseYaml } from 'yaml'

import {
  K8supgradeK8sCluster,
  K8supgradeK8sInfo,
  K8supgradeUpdateTaskK8sClusterReq
} from 'apiClient/services/devops/interface'

import {
  updateK8sUpgradeTaskCluster,
  useGetK8sUpgradeTaskCluster
} from '../../../../apiClient/services/devops'

import { CrMetadata } from './CrMetadata'
import { CrYamlEditor } from './CrYamlEditor'
import { StepsEditor } from './StepsEditor'
import {
  estimateDurationFromSteps,
  validateGoDuration
} from './upgradeDuration'
import {
  EMPTY_NODEPOOLS_TARGET,
  MaintenancePolicyMode,
  parseUpgradeOperationCrDocument,
  replaceUpgradeOperationCrDocument,
  UpgradeOperationCrStep,
  validateMaintenancePolicyMode,
  validateUpgradeOperationCrSteps
} from './upgradeOperationCr'

type YamlParseError = {
  message?: string
  makePretty?: () => void
  linePos?: { start?: { line?: number; col?: number } }
}

function getSubmitErrorMessage(error: unknown): string {
  const e = error as {
    response?: {
      data?: { errmsg?: string; message?: string; errcode?: string }
    }
    message?: string
  }
  const errmsg = e?.response?.data?.errmsg || e?.response?.data?.message
  const errcode = e?.response?.data?.errcode
  if (errcode && errmsg) return `${errcode}: ${errmsg}`
  if (errmsg) return errmsg
  if (e?.message) return e.message
  return 'update k8s cluster UpgradeOperationCR failed'
}

function formatYamlValidationError(error: unknown): string {
  const err = error as YamlParseError
  if (typeof err?.makePretty === 'function') {
    err.makePretty()
  }

  let message = (err?.message || 'Invalid YAML format').trim()
  const atLineIndex = message.indexOf(' at line ')
  if (atLineIndex > 0) {
    message = message.slice(0, atLineIndex).trim()
  } else {
    message = message.split('\n')[0].trim()
  }

  if (message.includes('Map keys must be unique')) {
    message = 'duplicate keys found'
  }

  const line = err.linePos?.start?.line
  if (line) {
    return `Invalid YAML (line ${line}): ${message}`
  }

  if (message.length > 160) {
    return `Invalid YAML: ${message.slice(0, 160)}...`
  }

  return `Invalid YAML: ${message}`
}

function validateUpgradeOperationCrYaml(value: string): string {
  const trimmed = value.trim()
  if (!trimmed) {
    return 'UpgradeOperationCR content cannot be empty'
  }

  try {
    parseYaml(trimmed, { prettyErrors: false })
  } catch (error) {
    return formatYamlValidationError(error)
  }

  return ''
}

function buildCrFromUiState(
  metadataSource: string,
  steps: UpgradeOperationCrStep[],
  duration: string,
  maintenancePolicyMode?: MaintenancePolicyMode
): string {
  return replaceUpgradeOperationCrDocument(metadataSource, {
    steps,
    duration,
    maintenancePolicyMode
  })
}

type CrViewTab = 'ui' | 'yaml'

const STEPS_TABLE_COLUMNS = [
  {
    title: '#',
    width: 52,
    align: 'right' as const,
    render: (_: unknown, __: unknown, index: number) => index + 1
  },
  { title: 'Target', dataIndex: 'target', width: 280 },
  { title: 'Version', dataIndex: 'version', width: 104 },
  {
    title: 'Cluster ID',
    dataIndex: 'cluster_id',
    width: 220,
    render: (value?: string) => value || '-'
  },
  {
    title: 'Options',
    width: 180,
    render: (_: unknown, record: UpgradeOperationCrStep) => {
      if (record.target !== EMPTY_NODEPOOLS_TARGET || !record.options) {
        return '-'
      }
      const parts: string[] = []
      if (record.options.delay) {
        parts.push(`delay: ${record.options.delay}`)
      }
      if (record.options.delete) {
        parts.push('delete: true')
      }
      return parts.length ? parts.join(', ') : '-'
    }
  }
]

export const CR: React.FC<
  React.PropsWithChildren<{
    k8sCluster: K8supgradeK8sCluster
    k8sInfo?: K8supgradeK8sInfo
    showEdit: boolean
  }>
> = ({ k8sCluster, k8sInfo, showEdit }) => {
  const [editing, setEditing] = useState<boolean>(false)
  const [loading, setLoading] = useState<boolean>(false)
  const [editable, setEditable] = useState<boolean>(true)
  const [editSnapshot, setEditSnapshot] = useState<K8supgradeK8sCluster | null>(
    null
  )
  const { task_id } = useParams<{ task_id: string }>()
  const { k8s_name } = useParams<{ k8s_name: string }>()
  const { refetch } = useGetK8sUpgradeTaskCluster(task_id, k8s_name, {
    query: { refetchOnWindowFocus: false, refetchInterval: false }
  })

  const parsedCr = useMemo(
    () => parseUpgradeOperationCrDocument(k8sCluster.upgrade_operation_cr),
    [k8sCluster.upgrade_operation_cr]
  )

  const startEditing = () => {
    setEditSnapshot({ ...k8sCluster })
    setEditing(true)
  }

  const cancelEditing = () => {
    setEditSnapshot(null)
    setEditing(false)
  }

  const reload = async () => {
    try {
      setLoading(true)
      await refetch()
      setEditSnapshot(null)
      setEditing(false)
    } catch (e) {
      console.log('refetch record failed', e)
    } finally {
      setLoading(false)
    }
  }

  return (
    <div>
      {editing && editSnapshot ? (
        <UpgradeOperationCREdit
          snapshot={editSnapshot}
          k8sInfo={k8sInfo}
          loading={loading}
          onCancel={cancelEditing}
          postSubmit={() => {
            setEditSnapshot(null)
            setEditing(false)
          }}
          reload={reload}
        />
      ) : (
        <>
          <UpgradeOperationCRViewOnly
            parsedCr={parsedCr}
            text={k8sCluster.upgrade_operation_cr || ''}
          />
          {showEdit ? (
            <Button
              type="primary"
              onClick={startEditing}
              disabled={!editable}
              style={{ marginTop: 8 }}
            >
              Edit
            </Button>
          ) : null}
        </>
      )}
    </div>
  )
}

function UpgradeOperationCREdit({
  snapshot,
  k8sInfo,
  loading,
  onCancel,
  postSubmit,
  reload
}: {
  snapshot: K8supgradeK8sCluster
  k8sInfo?: K8supgradeK8sInfo
  loading: boolean
  onCancel: () => void
  postSubmit: () => void
  reload: () => void
}) {
  const baseCr = snapshot.upgrade_operation_cr ?? ''
  const initialDoc = useMemo(
    () => parseUpgradeOperationCrDocument(baseCr),
    [baseCr]
  )
  const [activeTab, setActiveTab] = useState<CrViewTab>('ui')
  const [submitting, setSubmitting] = useState(false)
  const [submitError, setSubmitError] = useState('')
  const [steps, setSteps] = useState<UpgradeOperationCrStep[]>(
    () => initialDoc?.steps ?? []
  )
  const [duration, setDuration] = useState(() => initialDoc?.duration ?? '')
  const [maintenancePolicyMode, setMaintenancePolicyMode] = useState(
    () => initialDoc?.maintenancePolicyMode ?? ''
  )
  const [advancedYaml, setAdvancedYaml] = useState(baseCr)

  const clusterIds = useMemo(
    () =>
      (k8sInfo?.clusters || [])
        .map((cluster) => cluster.id)
        .filter((id): id is string => !!id),
    [k8sInfo?.clusters]
  )

  const metadataDoc = useMemo(() => {
    const base = parseUpgradeOperationCrDocument(advancedYaml) || initialDoc
    if (!base) {
      return null
    }
    return {
      ...base,
      duration,
      steps,
      ...(maintenancePolicyMode
        ? {
            maintenancePolicyMode:
              maintenancePolicyMode as MaintenancePolicyMode
          }
        : {})
    }
  }, [advancedYaml, duration, initialDoc, maintenancePolicyMode, steps])

  const stepValidationError = useMemo(
    () => validateUpgradeOperationCrSteps(steps, clusterIds),
    [clusterIds, steps]
  )

  const durationError = useMemo(() => validateGoDuration(duration), [duration])

  const maintenancePolicyModeError = useMemo(
    () => validateMaintenancePolicyMode(maintenancePolicyMode),
    [maintenancePolicyMode]
  )

  const yamlError = useMemo(
    () =>
      activeTab === 'yaml' ? validateUpgradeOperationCrYaml(advancedYaml) : '',
    [activeTab, advancedYaml]
  )

  const validationError =
    activeTab === 'ui'
      ? stepValidationError || durationError || maintenancePolicyModeError
      : yamlError

  const syncAdvancedYaml = (
    nextSteps: UpgradeOperationCrStep[],
    nextDuration: string,
    nextMaintenancePolicyMode?: MaintenancePolicyMode
  ) => {
    try {
      setAdvancedYaml(
        buildCrFromUiState(
          advancedYaml,
          nextSteps,
          nextDuration,
          nextMaintenancePolicyMode
        )
      )
    } catch {
      // keep current yaml if rebuild fails
    }
  }

  const handleStepsChange = (
    nextSteps: UpgradeOperationCrStep[],
    options?: { recalculateDuration?: boolean }
  ) => {
    const shouldRecalculate = options?.recalculateDuration !== false
    const nextDuration = shouldRecalculate
      ? estimateDurationFromSteps(nextSteps, k8sInfo)
      : duration

    setSteps(nextSteps)
    setDuration(nextDuration)
    syncAdvancedYaml(
      nextSteps,
      nextDuration,
      maintenancePolicyMode
        ? (maintenancePolicyMode as MaintenancePolicyMode)
        : undefined
    )
    if (submitError) {
      setSubmitError('')
    }
  }

  const handleDurationChange = (value: string) => {
    setDuration(value)
    syncAdvancedYaml(
      steps,
      value,
      maintenancePolicyMode
        ? (maintenancePolicyMode as MaintenancePolicyMode)
        : undefined
    )
    if (submitError) {
      setSubmitError('')
    }
  }

  const handleMaintenancePolicyModeChange = (value: string) => {
    setMaintenancePolicyMode(value)
    syncAdvancedYaml(
      steps,
      duration,
      value ? (value as MaintenancePolicyMode) : undefined
    )
    if (submitError) {
      setSubmitError('')
    }
  }

  const handleTabChange = (key: string) => {
    if (key === 'ui' && activeTab === 'yaml') {
      const doc = parseUpgradeOperationCrDocument(advancedYaml)
      if (doc) {
        setSteps(doc.steps)
        setDuration(doc.duration ?? '')
        setMaintenancePolicyMode(doc.maintenancePolicyMode ?? '')
      }
    }
    if (key === 'yaml' && activeTab === 'ui') {
      syncAdvancedYaml(
        steps,
        duration,
        maintenancePolicyMode
          ? (maintenancePolicyMode as MaintenancePolicyMode)
          : undefined
      )
    }
    setActiveTab(key as CrViewTab)
    setSubmitError('')
  }

  const resolveSubmitCr = () => {
    if (activeTab === 'yaml') {
      return advancedYaml
    }
    return buildCrFromUiState(
      advancedYaml,
      steps,
      duration,
      maintenancePolicyMode
        ? (maintenancePolicyMode as MaintenancePolicyMode)
        : undefined
    )
  }

  const handleFinish = async () => {
    if (validationError) return

    let submitCr = ''
    try {
      submitCr = resolveSubmitCr()
    } catch (e) {
      setSubmitError(
        e instanceof Error ? e.message : 'Failed to build UpgradeOperationCR'
      )
      return
    }

    const yamlValidation = validateUpgradeOperationCrYaml(submitCr)
    if (yamlValidation) {
      setSubmitError(yamlValidation)
      return
    }

    try {
      setSubmitting(true)
      setSubmitError('')
      const req: K8supgradeUpdateTaskK8sClusterReq = {
        id: snapshot.id,
        upgrade_operation_cr: submitCr,
        version: snapshot.version
      }

      try {
        await updateK8sUpgradeTaskCluster(
          snapshot.task_id!.toString(),
          snapshot.name!,
          req
        )
      } catch (e) {
        console.error(e)
        setSubmitError(getSubmitErrorMessage(e))
        return
      }
      postSubmit()
      await reload()
    } catch (err) {
      console.warn('Validate failed:', err)
      setSubmitError(getSubmitErrorMessage(err))
    } finally {
      setSubmitting(false)
    }
  }

  if (!initialDoc) {
    return (
      <div>
        <Tabs activeKey={activeTab} onChange={handleTabChange}>
          <Tabs.TabPane tab="UI" key="ui">
            <Typography.Text type="danger" style={{ display: 'block' }}>
              Failed to parse UpgradeOperationCR. Please use YAML tab to fix it.
            </Typography.Text>
          </Tabs.TabPane>
          <Tabs.TabPane tab="YAML" key="yaml">
            <CrYamlEditor
              value={advancedYaml}
              disabled={submitting || loading}
              invalid={!!yamlError}
              onChange={(value) => {
                setAdvancedYaml(value)
                if (submitError) setSubmitError('')
              }}
            />
          </Tabs.TabPane>
        </Tabs>
        {yamlError || submitError ? (
          <Typography.Text
            type="danger"
            style={{ display: 'block', marginTop: 8 }}
          >
            {yamlError || submitError}
          </Typography.Text>
        ) : null}
        <Space style={{ marginTop: 8 }}>
          <Button
            type="primary"
            loading={submitting}
            disabled={!!yamlError || loading}
            onClick={handleFinish}
          >
            Submit
          </Button>
          <Button onClick={onCancel} disabled={submitting || loading}>
            Cancel
          </Button>
        </Space>
      </div>
    )
  }

  return (
    <div>
      <Tabs activeKey={activeTab} onChange={handleTabChange}>
        <Tabs.TabPane tab="UI" key="ui">
          <CrMetadata
            doc={metadataDoc}
            editable
            duration={duration}
            durationError={durationError}
            onDurationChange={handleDurationChange}
            maintenancePolicyMode={maintenancePolicyMode}
            maintenancePolicyModeError={maintenancePolicyModeError}
            onMaintenancePolicyModeChange={handleMaintenancePolicyModeChange}
          />
          <StepsEditor
            steps={steps}
            k8sInfo={k8sInfo}
            clusterIds={clusterIds}
            disabled={submitting || loading}
            onChange={handleStepsChange}
          />
        </Tabs.TabPane>
        <Tabs.TabPane tab="YAML" key="yaml">
          <CrYamlEditor
            value={advancedYaml}
            disabled={submitting || loading}
            invalid={!!yamlError}
            onChange={(value) => {
              setAdvancedYaml(value)
              if (submitError) setSubmitError('')
            }}
          />
        </Tabs.TabPane>
      </Tabs>

      {validationError || submitError ? (
        <div style={{ marginTop: 8 }}>
          {validationError ? (
            <Typography.Text type="danger" style={{ display: 'block' }}>
              {validationError}
            </Typography.Text>
          ) : null}
          {submitError ? (
            <Typography.Text
              type="danger"
              style={{ display: 'block', marginTop: validationError ? 4 : 0 }}
            >
              {submitError}
            </Typography.Text>
          ) : null}
        </div>
      ) : null}

      <Space style={{ marginTop: 8 }}>
        <Button
          type="primary"
          loading={submitting}
          disabled={!!validationError || loading}
          onClick={handleFinish}
        >
          Submit
        </Button>
        <Button onClick={onCancel} disabled={submitting || loading}>
          Cancel
        </Button>
      </Space>
    </div>
  )
}

function UpgradeOperationCRViewOnly({
  parsedCr,
  text
}: {
  parsedCr: ReturnType<typeof parseUpgradeOperationCrDocument>
  text: string
}) {
  const [activeTab, setActiveTab] = useState<CrViewTab>('ui')

  return (
    <Tabs
      activeKey={activeTab}
      onChange={(key) => setActiveTab(key as CrViewTab)}
    >
      <Tabs.TabPane tab="UI" key="ui">
        {parsedCr ? (
          <>
            <CrMetadata doc={parsedCr} />
            <Table
              size="small"
              pagination={false}
              tableLayout="fixed"
              rowKey={(_, index) => String(index)}
              dataSource={parsedCr.steps.map((step, index) => ({
                ...step,
                key: index
              }))}
              columns={STEPS_TABLE_COLUMNS}
              scroll={{ x: 940 }}
            />
          </>
        ) : (
          <Typography.Text type="danger" style={{ display: 'block' }}>
            Failed to parse UpgradeOperationCR. Please check YAML tab.
          </Typography.Text>
        )}
      </Tabs.TabPane>
      <Tabs.TabPane tab="YAML" key="yaml">
        <CrYamlEditor value={text} readOnly />
      </Tabs.TabPane>
    </Tabs>
  )
}
