<template>
  <div
    :class="[
      page === 'list' ? 'section-header' : '',
      page === 'form' ? 'form-part-title' : ''
    ]">
    <i class="ico-c ico-section back-grad-title" v-if="icon !== undefined && icon !== ''">
      <svg height="20">
        <use :href="icourl + '#' + icon" />
      </svg>
    </i>
    <h1 v-if="title !== undefined && title !== ''">
      <span>{{ title }}</span>
      <span class="section-header-count">{{ localTotalRecord }} {{ listText }}</span>
    </h1>
    <h4 v-if="formTitle !== undefined && formTitle !== ''">{{ formTitle }}</h4>
    <div class="section-header-buttons-c collapsable" v-if="!isPreview">
      <div class="header-extra-buttons-C"><slot name="extraButtons"></slot></div>
      <div class="list-search-c" v-if="search">
        <i
          class="ico-c ico-section ico-section-header-btn btn-list-search menu-have-sub"
          @click="OpenSearchForm($event)">
          <svg>
            <use href="@/assets/images/icons.svg#search" />
          </svg>
        </i>
        <div
          v-if="searchForm"
          class="menu-sub menu-sub-top menu-sub-right menu-sub-mobile-fix section-header-search"
          :style="searchFieldPos">
          <div id="list-search-form">
            <input
              :id="'listsearch' + rnd"
              type="text"
              placeholder="Ara"
              v-model="localQuery"
              @keyup="SearchInputKeyUp" />
            <button @click="SearchQuery">
              <svg>
                <use href="@/assets/images/icons.svg#search" />
              </svg>
            </button>
          </div>
        </div>
      </div>
      <div class="header-buttons-C">
        <div class="menu-have-sub section-header-mobile-menu" @click="OpenMobileButtons">
          <i class="ico-c ico-section ico-section-header-btn">
            <svg>
              <use href="@/assets/images/icons.svg#oplist" />
            </svg>
          </i>
        </div>

        <div
          class="section-header-buttons menu-sub menu-sub-right menu-sub-top"
          v-if="mobileButtons">
          <icon-button
            v-if="export"
            classList="ico-section ico-section-header-btn"
            @click="exportPanel = !exportPanel"
            icon="export" />
          <icon-button
            v-if="filterable"
            classList="ico-section ico-section-header-btn"
            @click="OpenFilterPanel"
            icon="filter" />
          <icon-button
            v-if="addRoute !== undefined && addRoute !== ''"
            classList="ico-section ico-section-header-btn"
            :toRoute="addRoute"
            icon="plus" />

          <icon-button
            v-else-if="addAction !== undefined && addAction !== ''"
            classList="ico-section ico-section-header-btn"
            @click="localAddAction"
            icon="plus" />
        </div>
      </div>
    </div>
  </div>
  <div
    :class="[
      page === 'list' ? 'section-content section-inner' : '',
      page === 'form' ? 'form-part-content' : ''
    ]">
    <div class="list-filter-wrapper" v-if="haveFilter || searched">
      <h4>Filtreler ve Arama</h4>
      <div class="list-filter-content">
        <template v-if="localQuery !== ''">
          <div class="list-filter-item" data-filter="durum-onaylanmis">
            <strong>Arama:</strong>
            <span>
              {{ localQuery }}
            </span>
            <span class="list-filter-close" @click="RemoveSearch"></span>
          </div>
        </template>
        <template v-for="(filterA, index) in filterParams" :key="index">
          <template v-for="(filter, filterIndex) in filterA.values" :key="filterIndex">
            <div
              class="list-filter-item"
              data-filter="durum-onaylanmis"
              v-if="filterA.filter && (filter.text !== '' || filter.val !== '')">
              <strong>{{ filterA.title }}:</strong>
              <span>
                {{ filter.text || filter.val }} {{ filter.op === '=' ? '' : filter.op }}
              </span>
              <span
                class="list-filter-close"
                @click="RemoveFilterKey(index, filterIndex)"></span>
            </div>
          </template>
        </template>
      </div>
    </div>
    <slot name="datatable">
      <data-table
        :tableHeader="tableHeader"
        :tableData="localTableData"
        :rowAction="rowAction"
        v-model:sortData="localSort"
        v-model:pagination="localPagination"
        :rowNumber="rowNumber"
        :totalValues="localTotalValues"
        :isPreview="isPreview"
        :rowActions="rowActions"
        :actionFixed="actionFixed"
        :rowActionStyle="rowActionStyle"
        :isUseRoute="isUseRoute" />
    </slot>
  </div>

  <panel-wrapper v-if="filterPanel" v-model="filterPanel" :panel-title="'Filtreleme'">
    <template #panelContent>
      <panel-filter :filterHead="tableHeader" :filterParams="localFilterParams" />
    </template>
    <template #footerButton>
      <div class="button-c button-save" @click="FilterData">Filtrele</div>
    </template>
  </panel-wrapper>
  <panel-wrapper v-if="exportPanel" v-model="exportPanel" :panel-title="'Dışa Aktar'">
    <template #panelContent>
      <panel-export v-model:selectedExport="selectedExport" />
    </template>
  </panel-wrapper>
</template>

<script setup lang="ts">
  import { ref, reactive, onBeforeMount, watch, computed, nextTick } from 'vue'
  import axios from 'axios'
  import { useRouter, useRoute } from 'vue-router'
  const router = useRouter()
  const route = useRoute()

  import { useGlobalStore } from '@/stores/globalStore'
  const globalStore = useGlobalStore()
  import { useDataStore } from '@/stores/dataStore'
  const dataStore = useDataStore()

  import icourl from '@/assets/images/icons.svg'
  import PanelWrapper from '@/components/PanelWrapper.vue'
  import PanelFilter from './PanelFilter.vue'
  import PanelExport from './PanelExport.vue'

  interface ITableHead {
    [key: string]: any
    title: string
    name: string
    compute?: Function
    computeHtml?: Function
    sort?: boolean
    filter?: Record<string, any>
    style?: Record<string, any>
  }
  interface IPagination {
    [key: string]: any
    pageNumber?: number | string
    pageSize: number | string
    totalRecords: number | string
  }
  interface ISort {
    [key: string]: any
    sortColumn?: string
    sortOrder?: string
  }
  export interface Props {
    tableHeader: ITableHead[]
    tableData?: Record<string, any>[]
    pagination?: IPagination
    rowAction?: Function | string
    title?: string
    formTitle?: string
    listText?: string
    icon?: string
    apiList?: string
    addRoute?: string
    addAction?: Function | string
    filterData?: Record<string, any>
    sortData?: ISort
    totalRecord?: string | number
    query?: string
    apiText?: string
    page?: string
    refresh?: boolean
    export?: boolean
    search?: boolean
    rowNumber?: boolean
    totalValues?: Record<string, any>
    isPreview?: boolean
    rowActions?: Record<string, any>[]
    actionFixed?: boolean
    rowActionStyle?: string
    isUseRoute?: boolean
  }

  const props = withDefaults(defineProps<Props>(), {
    tableData: () => [],
    totalRecord: 0,
    apiText: '',
    page: 'list',
    refresh: false,
    export: true,
    search: true,
    rowNumber: false,
    isPreview: false,
    actionFixed: false,
    isUseRoute: false
  })

  const emit = defineEmits([
    'click',
    'update:tableData',
    'update:pagination',
    'update:sortData',
    'update:query',
    'update:refresh',
    'update:totalRecord'
  ])

  const rnd = ref<number>(Number(Math.floor(Math.random() * 1000000000)))
  const filterPanel = ref<boolean>(false)
  const exportPanel = ref<boolean>(false)
  const searchForm = ref<boolean>(false)
  const mobileButtons = ref<boolean>(false)
  const selectedExport = ref<number | null>(null)
  const searched = ref<boolean>(false)

  const localSort = ref<ISort>({} as ISort)
  const localPagination = ref<IPagination>({} as IPagination)
  const localTotalValues = reactive<Record<string, any>>({})
  const searchFieldPos = ref<string>('')
  const localLoaded = ref<boolean>(false)

  if (props.totalValues !== undefined) Object.assign(localTotalValues, props.totalValues)

  if (props.pagination !== undefined)
    Object.assign(localPagination.value, props.pagination)
  const localTableData = ref<Record<string, any>[]>([])
  if (props.tableData !== undefined) localTableData.value = props.tableData
  const localQuery = ref<string>('')

  const localTotalRecord = ref<string | number>(0)
  if (props.totalRecord !== undefined) localTotalRecord.value = props.totalRecord

  const localFilterParams = ref<Record<string, any>[]>([])
  const filterParams = ref<Record<string, any>[]>([])

  const OpenFilterPanel = () => {
    filterPanel.value = true
  }

  const EqualObjects = (source: Record<string, any>, dest: Record<string, any>) => {
    Object.keys(source).forEach((keys, is) => {
      if (dest[keys] === undefined) delete source[keys]
    })
  }
  const CreateFilterData = () => {
    props.tableHeader.forEach((d: Record<string, any>, ind: number) => {
      if (d.filter !== undefined) {
        const filterKey = d.filter.filterId || d.name
        const filterItem: Record<string, any> = {}
        const firstVal: Record<string, any> = {}
        const secondVal: Record<string, any> = {}

        filterItem.title = d.title
        filterItem.type = d.filter.type
        filterItem.between = d.filter.between || false
        filterItem.range = d.filter.range || false
        filterItem.key = filterKey
        filterItem.filter = false
        filterItem.hIndex = ind
        filterItem.values = []

        const opKey =
          d.filter.type === 'date'
            ? d.filter.between !== undefined && d.filter.between
              ? '>'
              : '='
            : '='
        firstVal.op = opKey
        firstVal.val = ''
        firstVal.text = ''
        filterItem.values.push(firstVal)
        if (d.filter.between !== undefined && d.filter.between) {
          secondVal.op = '<'
          secondVal.val = ''
          secondVal.text = ''
          filterItem.values.push(secondVal)
        }

        localFilterParams.value.push(filterItem)
      }
    })
  }

  const AddParamsToObject = (obj: Record<string, any>) => {
    filterParams.value.forEach((filter, index) => {
      if (filter.filter) {
        const key = 'Filters[' + filter.key + ']'
        if (obj[key] === undefined) obj[key] = [] as string[]
        filter.values.forEach((val: Record<string, any>, vIndex: number) => {
          if (val.val !== '') {
            ;(obj[key] as string[]).push(val.op + val.val)
          }
        })
      }
    })
  }
  const ResetFilterQueries = async () => {
    if (props.isUseRoute) {
      const newQuery: Record<string, any> = {
        ...route.query
      }
      Object.keys(newQuery).forEach((q) => {
        if (q.includes('Filters[')) delete newQuery[q]
      })
      await router.replace({ query: newQuery })
      AddParamsToObject(newQuery)
      await router.push({ query: newQuery })
    }
  }
  const FilterData = async () => {
    filterParams.value = JSON.parse(JSON.stringify(localFilterParams.value))
    ResetFilterQueries()
    setTimeout(async () => {
      await GetLocalData()
      filterPanel.value = false
    }, 50)
  }

  const localAddAction = () => {
    if (props.addAction !== undefined && props.addAction !== '')
      (props.addAction as Function)()
  }

  const filterable = computed(() =>
    props.tableHeader.some((obj: Record<string, any>) => obj.filter !== undefined)
  )

  const haveFilter = computed(() =>
    filterParams.value.some((obj: Record<string, any>) => obj.filter)
  )

  const RemoveFilterKey = (index: number, fIndex: number) => {
    const query: Record<string, any> = { ...route.query }

    filterParams.value[index].values[fIndex].val = ''
    filterParams.value[index].values[fIndex].text = ''

    const allEmpty: boolean = filterParams.value[index].values.every(
      (obj: Record<string, any>) => obj.val === ''
    )
    if (allEmpty) filterParams.value[index].filter = false

    localFilterParams.value = JSON.parse(JSON.stringify(filterParams.value))

    ResetFilterQueries()
    setTimeout(async () => {
      await GetLocalData()
      filterPanel.value = false
    }, 50)
  }
  const RemoveSearch = () => {
    localQuery.value = ''
    searched.value = false
    GetLocalData()
  }
  const pageNumberChanging = ref<boolean>(false)
  const sortChanging = ref<boolean>(false)
  const searchChanging = ref<boolean>(false)
  const filterChanging = ref<boolean>(false)

  const isQueryEqual = (a: Record<string, any>, b: Record<string, any>) => {
    const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})])
    for (const k of keys) {
      const va = a?.[k]
      const vb = b?.[k]
      if (Array.isArray(va) && Array.isArray(vb)) {
        if (va.length !== vb.length || va.some((v: any, i: number) => v !== vb[i]))
          return false
      } else if (String(va ?? '') !== String(vb ?? '')) return false
    }
    return true
  }

  const RoutePageControl = () => {
    if (props.isUseRoute) {
      const q = { ...route.query } as Record<string, any>
      pageNumberChanging.value = true

      if (q.pageNumber !== undefined && q.pageNumber !== '') {
        localPagination.value.pageNumber = Number(q.pageNumber)
      } else {
        q.pageNumber = String(1)
        localPagination.value.pageNumber = localPagination.value.pageNumber || 1
      }

      if (!isQueryEqual(q, route.query as Record<string, any>)) {
        router.push({ query: q })
      }
      nextTick(() => {
        pageNumberChanging.value = false
      })
    }
  }

  const RouteSortControl = () => {
    if (props.isUseRoute) {
      const q = { ...route.query } as Record<string, any>
      sortChanging.value = true
      if (q.sortOrder !== undefined && q.sortOrder !== null && q.sortOrder !== '') {
        localSort.value.sortColumn = q.sortColumn as string
        localSort.value.sortOrder = q.sortOrder as string
      } else {
        delete q.sortOrder
        delete q.sortColumn
        delete localSort.value.sortColumn
        delete localSort.value.sortOrder
      }
      if (!isQueryEqual(q, route.query as Record<string, any>)) {
        router.push({ query: q })
      }
      nextTick(() => {
        sortChanging.value = false
      })
    }
  }

  const RouteSearchControl = () => {
    if (props.isUseRoute) {
      const q = { ...route.query } as Record<string, any>
      searchChanging.value = true
      if (
        q.searchString !== undefined &&
        q.searchString !== null &&
        q.searchString !== ''
      ) {
        localQuery.value = q.searchString as string
      } else {
        localQuery.value = ''
        delete q.searchString
      }
      if (!isQueryEqual(q, route.query as Record<string, any>)) {
        router.push({ query: q })
      }
      nextTick(() => {
        searchChanging.value = false
      })
    }
  }

  const RouteFilterControl = () => {
    if (props.isUseRoute) {
      filterChanging.value = true
      if (Object.keys(route.query).length > 0) {
        Object.keys(route.query).forEach((key) => {
          if (key.includes('Filters[')) {
            const newKey = key.match(/(?<=\[)[^\]]+(?=\])/)![0]

            localFilterParams.value.forEach((filter: Record<string, any>) => {
              if (filter.key === newKey) {
                if (!filter.filter) {
                  const isArray = Array.isArray(route.query[key])
                  if (isArray) {
                    ;(route.query[key]! as string[]).forEach((val, ind) => {
                      filter.values[ind].op = (route.query[key]! as string[])[ind].charAt(
                        0
                      )
                      filter.values[ind].val = (route.query[key]! as string[])[ind].slice(
                        1
                      )
                      if (filter.type === 'select') {
                        const filterData = props.tableHeader[filter.hIndex].filter!.data
                        if (filterData && Array.isArray(filterData)) {
                          const forText = filterData.find(
                            (o: Record<string, any>) =>
                              String(
                                o[props.tableHeader[filter.hIndex].filter!.listVal]
                              ) === String(filter.values[ind].val)
                          )
                          if (forText) {
                            filter.values[ind].text =
                              forText[props.tableHeader[filter.hIndex].filter!.listText]
                          } else {
                            filter.values[ind].text = filter.values[ind].val
                          }
                        } else {
                          filter.values[ind].text = filter.values[ind].val
                        }
                      }
                    })
                  } else {
                    filter.values[0].op = (route.query[key] as string).charAt(0)
                    filter.values[0].val = (route.query[key] as string).slice(1)

                    if (filter.type === 'select') {
                      const filterData = props.tableHeader[filter.hIndex].filter!.data
                      if (filterData && Array.isArray(filterData)) {
                        const forText = filterData.find(
                          (o: Record<string, any>) =>
                            String(o[props.tableHeader[filter.hIndex].filter!.listVal]) ===
                            String(filter.values[0].val)
                        )
                        if (forText) {
                          filter.values[0].text =
                            forText[props.tableHeader[filter.hIndex].filter!.listText]
                        } else {
                          filter.values[0].text = filter.values[0].val
                        }
                      } else {
                        filter.values[0].text = filter.values[0].val
                      }
                    }
                  }
                  filter.filter = true
                }
              }
            })
          }
        })

        filterParams.value = JSON.parse(JSON.stringify(localFilterParams.value))
      }
      nextTick(() => {
        filterChanging.value = false
      })
    }
  }

  const GetLocalData = async () => {
    localLoaded.value = false
    if (selectedExport.value !== null) {
      let exportUrl = ''
      let fileType = ''
      let fileName = ''

      // Export tipine göre URL ve dosya ayarları
      if (selectedExport.value === 1) {
        // PDF
        exportUrl = axios.defaults.baseURL + dataStore.apiBase + props.apiList + '/Pdf'
        fileType = 'application/pdf'
        fileName = 'export.pdf'
      } else if (selectedExport.value === 2) {
        // Excel
        exportUrl = axios.defaults.baseURL + dataStore.apiBase + props.apiList + '/Excel'
        fileType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        fileName = 'export.xlsx'
      }

      // Export için parametreleri hazırla
      let exportParams: Record<string, any> = {}

      // Sıralama parametrelerini ekle
      if (localSort.value.sortColumn !== undefined) {
        exportParams.sortColumn = localSort.value.sortColumn
        if (localSort.value.sortOrder !== undefined && localSort.value.sortOrder !== '')
          exportParams.sortOrder = localSort.value.sortOrder
      }

      // Arama parametresini ekle
      if (localQuery.value !== '') {
        exportParams.searchString = localQuery.value
      }

      // Filtre parametrelerini ekle
      if (haveFilter.value) AddParamsToObject(exportParams)

      // Axios ile dosya indirme - interceptor otomatik olarak token ekleyecek
      try {
        const response = await axios.get(exportUrl, {
          params: exportParams,
          responseType: 'blob'
        })

        const blob = new Blob([response.data], { type: fileType })
        const url = window.URL.createObjectURL(blob)
        const link = document.createElement('a')
        link.href = url
        link.download = fileName
        document.body.appendChild(link)
        link.dispatchEvent(
          new MouseEvent('click', { bubbles: true, cancelable: true, view: window })
        )
        link.remove()
        window.URL.revokeObjectURL(url)
      } catch (error) {
        console.error('Export error:', error)
      }

      selectedExport.value = null
      exportPanel.value = false
    } else {
      let apiData: Record<string, any> = {
        params: {
          pageNumber: props.isPreview ? 0 : localPagination.value.pageNumber || 1,
          pageSize: globalStore.perPage
        }
      }

      if (localSort.value.sortColumn !== undefined) {
        apiData.params.sortColumn = localSort.value.sortColumn
        if (localSort.value.sortOrder !== undefined && localSort.value.sortOrder !== '')
          apiData.params.sortOrder = localSort.value.sortOrder
      }
      if (localQuery.value !== '') {
        apiData.params.searchString = localQuery.value
      } else {
        delete apiData.params.searchString
      }

      if (props.apiText !== undefined && props.apiText !== '')
        apiData.text = props.apiText

      var filterparam: Record<string, any> = {}

      if (haveFilter.value) {
        AddParamsToObject(filterparam)
        Object.assign(apiData.params, filterparam)
      }
      let dt = await dataStore.dataGet(props.apiList as string, apiData)

      // buraya dikkat cekilis itiraz modulunde liste duzgun calışması için nullable kullanıldı
      if (dt !== 'errorfalse') localTableData.value = dt.data ?? dt
      else localTableData.value = []

      localPagination.value.totalRecords = dt.totalRecords
      localPagination.value.pageSize = dt.pageSize

      localTotalRecord.value = dt.totalRecords || localTableData.value.length

      if (props.pagination !== undefined) emit('update:pagination', localPagination.value)
      if (props.totalRecord !== undefined)
        emit('update:totalRecord', localTotalRecord.value)
      if (props.totalValues !== undefined) {
        Object.keys(props.totalValues).forEach((key, i) => {
          localTotalValues[key] = dt[props.totalValues![key]]
        })
      }
      emit('update:refresh', false)
    }
    nextTick(() => {
      localLoaded.value = true
    })
  }
  const OpenMobileButtons = () => {
    searchForm.value = false
    mobileButtons.value = !mobileButtons.value
  }
  const OpenSearchForm = (e: Event) => {
    searchFieldPos.value = ''
    if (globalStore.screenWidth <= globalStore.breakPoints.tabletp) {
      mobileButtons.value = false
      let eltRect = (e.target as HTMLElement).getBoundingClientRect() as DOMRect
      searchFieldPos.value = `top:${eltRect.top + eltRect.height}px;`
    }

    searchForm.value = !searchForm.value
    if (searchForm.value) {
      setTimeout(() => {
        let el: HTMLInputElement = document.getElementById(
          'listsearch' + rnd.value
        ) as HTMLInputElement
        el.focus()
        el.select()
      }, 50)
    }
  }
  const SearchInputKeyUp = (e: KeyboardEvent) => {
    if (e.key === 'Enter') SearchQuery()
  }
  const SearchQuery = () => {
    if (props.apiList !== undefined) GetLocalData()
    else emit('update:query', localQuery.value)
    if (props.isUseRoute) {
      router.push({
        query: {
          ...route.query,
          searchString: localQuery.value
        }
      })
    }
    searchForm.value = false
    searched.value = true
  }
  const Resize = () => {
    searchForm.value = false
    mobileButtons.value = false
  }

  onBeforeMount(() => {
    if (globalStore.screenWidth >= globalStore.breakPoints.tabletp)
      mobileButtons.value = true

    CreateFilterData()
    RoutePageControl()
    RouteSortControl()
    RouteSearchControl()
    RouteFilterControl()

    if (props.apiList !== undefined) GetLocalData()
    window.addEventListener('resize', Resize)
  })

  watch(
    () => [localSort.value],
    () => {
      if (!sortChanging.value) GetLocalData()
    },
    { deep: true }
  )
  watch(
    () => [route],
    () => {
      RoutePageControl()
      RouteSortControl()
      RouteSearchControl()
      RouteFilterControl()
      GetLocalData()
    },
    { deep: true }
  )
  watch(
    () => localPagination.value.pageNumber,
    () => {
      if (!pageNumberChanging.value) {
        GetLocalData()
      }
    }
  )
  watch(
    () => props.refresh,
    () => {
      if (props.refresh) GetLocalData()
    }
  )
  watch(
    () => localQuery.value,
    () => {
      if (props.apiList !== undefined && localQuery.value.length === 0) {
        if (!searchChanging.value) GetLocalData()
      } else if (localQuery.value.length === 0) emit('update:query', localQuery.value)
    }
  )
  watch(
    () => selectedExport.value,
    () => {
      if (selectedExport.value !== null) GetLocalData()
    }
  )
</script>
