import { Notification, toast } from '@/components/ui'
import { getList } from '@/services/form.service'
import { useListFormColumns } from '@/shared/useListFormColumns'
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { usePermission } from '@/utils/hooks/usePermission'
import { Form as FormDx } from 'devextreme-react/form'
import { captionize } from 'devextreme/core/utils/inflector'
import CustomStore from 'devextreme/data/custom_store'
import { GroupItem } from 'devextreme/ui/form'
import { useEffect, useRef, useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { GridColumnData } from '../list/GridColumnData'
import { addCss, addJs } from '../list/Utils'
import { PermissionResults, RowMode, SimpleItemWithColData } from './types'
import { EditingFormItemDto, GridDto, PlatformEditorTypes } from '@/proxy/form/models'
import { getAccessDeniedPath } from '@/utils/routing'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { useLookupDataSource } from '../form/useLookupDataSource'

const useGridData = (props: {
  mode: RowMode
  listFormCode: string
  id?: string
  level?: number
  isSubForm?: boolean
  onSubmitAction?: () => void
}) => {
  const { mode, listFormCode, id, isSubForm } = props

  const [gridReady, setGridReady] = useState(false)
  const [loading, setLoading] = useState(false)
  const [filter, setFilter] = useState<any[]>([])
  const [gridDto, setGridDto] = useState<GridDto>()
  const [dataSource, setDataSource] = useState<CustomStore<any, any>>()
  const [commandColumnData, setCommandColumnData] = useState<GridColumnData>()
  const [formDataOld, setFormDataOld] = useState<any>()
  const [formData, setFormData] = useState<any>()
  const [formItems, setFormItems] = useState<GroupItem[]>([])
  const [permissionResults, setPermissionResults] = useState<PermissionResults>()

  const refForm = useRef<FormDx>(null)
  const [searchParams] = useSearchParams()
  const navigate = useNavigate()
  const { translate } = useLocalization()
  const { checkPermission } = usePermission()
  const { getBandedColumns } = useListFormColumns({
    gridDto,
    listFormCode,
    isSubForm,
    gridRef: undefined,
  })
  const { createSelectDataSource } = useListFormCustomDataSource({})
  const { getLookupDataSource } = useLookupDataSource({ listFormCode, isSubForm })

  const fetchData = async () => {
    setLoading(true)
    try {
      const response: any = await dataSource?.load({
        filter,
        skip: 0,
        take: 1,
      })
      if (response?.data?.length) {
        setFormData(response.data[0])
        setFormDataOld({ ...response.data[0] })
      } else {
        setFormData(undefined)
        setFormDataOld(undefined)
      }
    } catch (error: any) {
      toast.push(<Notification title={error.message} type="danger" />, {
        placement: 'top-end',
      })
    } finally {
      setLoading(false)
    }
  }

  const handleSubmit = async (e: any) => {
    e.preventDefault()
    if (!dataSource) {
      return
    }

    const validationResult = refForm.current?.instance.validate()
    if (!validationResult?.isValid) {
      return
    }

    setLoading(true)
    try {
      const formValues = { ...formData }
      if (mode === 'new') {
        const result = await dataSource.insert(formValues)
        if (result.data) {
          if (!isSubForm) {
            navigate(result.data)
          } else if (props.onSubmitAction) {
            props.onSubmitAction()
          }
          toast.push(
            <Notification type="success" duration={2000}>
              {translate('::ListForms.FormBilgileriKaydedildi')}
            </Notification>,
            {
              placement: 'top-end',
            },
          )
        } else {
          throw new Error(translate('::ListForms.FormBilgileriKaydedilemedi'))
        }
      } else if (mode === 'edit') {
        let data: any = {}
        if (gridDto?.gridOptions.editingOptionDto?.sendOnlyChangedFormValuesUpdate) {
          Object.keys(formValues).forEach((key) => {
            if (formValues[key] !== formDataOld[key]) {
              data[key] = formValues[key]
            }
          })
        } else {
          data = { ...formValues }
        }

        if (gridDto?.gridOptions.keyFieldName) {
          delete data[gridDto?.gridOptions.keyFieldName]
        }

        var result = await dataSource.update(id, data)
        if (result.data > 0) {
          if (!isSubForm) {
            navigate(
              ROUTES_ENUM.protected.admin.formView
                .replace(':listFormCode', listFormCode)
                .replace(':id', id!),
            )
          } else if (props.onSubmitAction) {
            props.onSubmitAction()
          }
          toast.push(
            <Notification type="success" duration={2000}>
              {translate('::ListForms.FormBilgileriKaydedildi')}
            </Notification>,
            {
              placement: 'top-end',
            },
          )
        } else {
          throw new Error(translate('::ListForms.FormBilgileriKaydedilemedi'))
        }
      }
    } catch (error: any) {
      toast.push(<Notification title={error.message} type="danger" />, {
        placement: 'top-end',
      })
    } finally {
      setLoading(false)
    }
  }

  useEffect(() => {
    setGridReady(false)
    const initializeGrid = async () => {
      const response = await getList({ listFormCode })
      setGridDto(response.data)
    }

    initializeGrid()
  }, [listFormCode])

  useEffect(() => {
    setGridReady(false)
    if (!gridDto) {
      return
    }

    setPermissionResults({
      c:
        gridDto?.gridOptions.editingOptionDto.allowAdding === true &&
        checkPermission(gridDto?.gridOptions.permissionDto.c),
      r: checkPermission(gridDto?.gridOptions.permissionDto.r),
      u:
        gridDto?.gridOptions.editingOptionDto.allowUpdating === true &&
        checkPermission(gridDto?.gridOptions.permissionDto.u),
      d:
        gridDto?.gridOptions.editingOptionDto.allowDeleting === true &&
        checkPermission(gridDto?.gridOptions.permissionDto.d),
      e: checkPermission(gridDto?.gridOptions.permissionDto.e),
      i: checkPermission(gridDto?.gridOptions.permissionDto.i),
    })

    // Set js and css
    const grdOpt = gridDto.gridOptions
    grdOpt.customJsSources.forEach(addJs)
    grdOpt.customStyleSources.forEach(addCss)

    // Set columns
    const cols = getBandedColumns()
    setCommandColumnData(cols?.find((a) => a.type == 'buttons'))

    // Set data source
    const dataSource: CustomStore<any, any> = createSelectDataSource(
      gridDto.gridOptions,
      listFormCode,
      searchParams,
      cols,
    )
    setDataSource(dataSource)

    const items = gridDto?.gridOptions.editingFormDto
      ?.sort((a: any, b: any) => {
        return a.order >= b.order ? 1 : -1
      })
      .map((e: any) => {
        return {
          itemType: e.itemType,
          colCount: e.colCount,
          colSpan: e.colSpan,
          caption: e.caption,
          items: e.items
            ?.sort((a: any, b: any) => {
              return a.order >= b.order ? 1 : -1
            })
            .map((i: EditingFormItemDto) => {
              let editorOptions = {}
              const colData = gridDto.columnFormats.find((x) => x.fieldName === i.dataField)

              try {
                editorOptions = i.editorOptions && JSON.parse(i.editorOptions)
              } catch {}
              const item: SimpleItemWithColData = {
                canRead:
                  gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canRead ??
                  false,
                canUpdate:
                  gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canUpdate ??
                  false,
                canCreate:
                  gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canCreate ??
                  false,
                canExport:
                  gridDto.columnFormats.find((x: any) => x.fieldName === i.dataField)?.canExport ??
                  false,
                dataField: i.dataField,
                name: i.dataField,
                editorType2: i.editorType2,
                editorType:
                  i.editorType2 == PlatformEditorTypes.dxGridBox ? 'dxDropDownBox' : i.editorType2,
                colSpan: i.colSpan,
                isRequired: i.isRequired,
                editorOptions: {
                  ...editorOptions,
                  ...(colData?.lookupDto?.dataSourceType
                    ? {
                        dataSource: getLookupDataSource(colData?.editorOptions, colData, formData),
                        valueExpr: colData?.lookupDto?.valueExpr?.toLowerCase(),
                        displayExpr: colData?.lookupDto?.displayExpr?.toLowerCase(),
                      }
                    : {}),
                },
                colData,
                tagBoxOptions: i.tagBoxOptions,
                gridBoxOptions: i.gridBoxOptions,
                script: i.script,
              }
              if (i.dataField.indexOf(':') >= 0) {
                item.label = { text: captionize(i.dataField.split(':')[1]) }
              }
              if ((mode == 'edit' && !item.canUpdate) || (mode == 'new' && !item.canCreate)) {
                item.editorOptions = {
                  ...item.editorOptions,
                  readOnly: true,
                }
              }
              return item
            })
            .filter((a: any) => {
              if (mode === 'view') {
                return a.canRead
              } else if (mode === 'new') {
                return a.canCreate || a.canRead
              } else if (mode === 'edit') {
                return a.canUpdate || a.canRead
              } else {
                return false
              }
            }),
        } as GroupItem
      })
    setFormItems(items)
    setGridReady(true)
  }, [gridDto, formData])

  useEffect(() => {
    if (!gridReady) {
      return
    }

    if (mode !== 'new') {
      setFilter([gridDto?.gridOptions.keyFieldName ?? 'Id', '=', id])
    }
  }, [id, gridReady])

  useEffect(() => {
    if (filter?.length) {
      fetchData()
    }
  }, [filter])

  // Auth check
  useEffect(() => {
    if (!permissionResults) return

    const noCreate = mode === 'new' && !permissionResults.c
    const noUpdate = mode === 'edit' && !permissionResults.u
    const noRead = mode === 'view' && !permissionResults.r

    if (noCreate || noUpdate || noRead) {
      navigate(getAccessDeniedPath(location.pathname), { replace: true, state: { from: location } })
    }
  }, [permissionResults])

  return {
    loading,
    gridDto,
    dataSource,
    commandColumnData,
    filter,
    formItems,
    formData,
    refForm,
    permissionResults,
    fetchData,
    setFormData,
    handleSubmit,
  }
}

export { useGridData }
