import {
  type BaseType,
  type CalendarType,
  ExportTypes,
  type FilterType,
  type KanbanType,
  type MapType,
  type PaginatedType,
  type RequestParams,
  type SortType,
  type TableType,
  UITypes,
  type ViewType,
  ViewTypes,
} from 'nocodb-sdk'
import { setI18nLanguage } from '~/plugins/a.i18n'

export function useSharedView() {
  const router = useRouter()

  const nestedFilters = ref<(FilterType & { status?: 'update' | 'delete' | 'create'; parentId?: string })[]>([])

  const { appInfo } = useGlobal()

  const workspaceStore = useWorkspace()

  const baseStore = useBase()

  const basesStore = useBases()

  const { basesUser } = storeToRefs(basesStore)

  const { base } = storeToRefs(baseStore)

  const appInfoDefaultLimit = appInfo.value.defaultLimit || 50

  const paginationData = useState<PaginatedType>('paginationData', () => ({
    page: 1,
    pageSize: appInfoDefaultLimit,
  }))

  const sharedView = useState<(ViewType & { basePermissions: BaseType['permissions'] }) | undefined>(
    'sharedView',
    () => undefined,
  )

  const sorts = ref<SortType[]>([])

  const password = useState<string | undefined>('password', () => undefined)

  provide(SharedViewPasswordInj, password)

  const allowCSVDownload = useState<boolean>('allowCSVDownload', () => false)

  const meta = useState<TableType | KanbanType | MapType | CalendarType | undefined>('meta', () => undefined)

  const formColumns = computed(
    () =>
      (meta.value as TableType)?.columns
        ?.filter(
          (f: Record<string, any>) =>
            f.show &&
            f.uidt !== UITypes.Rollup &&
            f.uidt !== UITypes.Lookup &&
            f.uidt !== UITypes.Formula &&
            f.uidt !== UITypes.Barcode &&
            f.uidt !== UITypes.QrCode,
        )
        .sort((a: Record<string, any>, b: Record<string, any>) => a.order - b.order)
        .map((c: Record<string, any>) => ({ ...c, required: !!(c.required || 0) })) ?? [],
  )

  const { $api } = useNuxtApp()

  const { setMeta } = useMetas()

  const loadSharedView = async (viewId: string, localPassword: string | undefined = undefined) => {
    const viewMeta: Record<string, any> = await $api.public.sharedViewMetaGet(viewId, {
      headers: {
        'xc-password': localPassword ?? password.value,
      },
    })

    // Set workspace info if present
    if (viewMeta?.workspace) {
      workspaceStore.workspaces.set(viewMeta.workspace.id, viewMeta.workspace)
    }

    try {
      allowCSVDownload.value = parseProp(viewMeta.meta)?.allowCSVDownload
    } catch {
      allowCSVDownload.value = false
    }

    if (localPassword) password.value = localPassword
    sharedView.value = { title: '', ...viewMeta } as ViewType
    meta.value = { ...viewMeta.model }

    if (parseProp(viewMeta.meta)?.language) {
      setI18nLanguage(parseProp(viewMeta.meta).language)
    }

    let order = 1

    if (meta.value) {
      meta.value.columns = [...viewMeta.model.columns].map((c) => ({ ...c, order: order++ })).sort((a, b) => a.order - b.order)
    }

    await setMeta(viewMeta.model)

    // if base is not defined then set it with an object containing source
    if (!base.value?.sources)
      baseStore.setProject({
        id: viewMeta.base_id,
        sources: [
          {
            id: viewMeta.source_id,
            type: viewMeta.client,
            ...(viewMeta.source || {}),
          },
        ],
      })

    const relatedMetas = { ...viewMeta.relatedMetas }
    Object.keys(relatedMetas).forEach((key) => setMeta(relatedMetas[key]))

    if (viewMeta.users) {
      basesUser.value.set(viewMeta.base_id, viewMeta.users)
    }
  }

  const fetchSharedViewData = async (
    param: {
      sortsArr: SortType[]
      filtersArr: FilterType[]
      fields?: any[]
      sort?: any[]
      where?: string
      /** Query params for nested data */
      nested?: any
      offset?: number
      limit?: number
      whereTz?: string
    },
    opts?: {
      isGroupBy?: boolean
      isInfiniteScroll?: boolean
    },
  ) => {
    if (!sharedView.value)
      return {
        list: [],
        pageInfo: {},
      }

    if (!param.offset) {
      const page = paginationData.value.page || 1
      const pageSize = opts?.isInfiniteScroll
        ? param.limit
        : opts?.isGroupBy
        ? appInfo.value.defaultGroupByLimit?.limitRecord || 10
        : paginationData.value.pageSize || appInfoDefaultLimit
      param.offset = (page - 1) * pageSize
      param.limit = sharedView.value?.type === ViewTypes.MAP ? 1000 : pageSize
    }

    return await $api.public.dataList(
      sharedView.value.uuid!,
      {
        ...param,
        filterArrJson: stringifyFilterOrSortArr(param.filtersArr ?? nestedFilters.value),
        sortArrJson: stringifyFilterOrSortArr(param.sortsArr ?? sorts.value),
        include_row_color: true,
      } as any,
      {
        headers: {
          'xc-password': password.value,
        },
      },
    )
  }

  const fetchSharedCalendarViewData = async (param: {
    from_date: string
    to_date: string
    next_date: string
    prev_date: string
    sortsArr: SortType[]
    filtersArr: FilterType[]
    fields?: any[]
    sort?: any[]
    where?: string
    whereTz?: string
    /** Query params for nested data */
    nested?: any
    offset?: number
  }) => {
    if (!sharedView.value)
      return {
        list: [],
        pageInfo: {},
      }

    if (!param.offset) {
      const page = paginationData.value.page || 1
      const pageSize = paginationData.value.pageSize || appInfoDefaultLimit
      param.offset = (page - 1) * pageSize
    }

    return await $api.dbCalendarViewRow.publicDataCalendarRowList(
      sharedView.value.uuid!,
      {
        limit: sharedView.value?.type === ViewTypes.CALENDAR ? 3000 : undefined,
        ...param,
        filterArrJson: stringifyFilterOrSortArr(param.filtersArr ?? nestedFilters.value),
        sortArrJson: stringifyFilterOrSortArr(param.sortsArr ?? sorts.value),
        include_row_color: true,
      } as any,
      {
        headers: {
          'xc-password': password.value,
        },
      },
    )
  }

  const fetchAggregatedData = async (param: {
    aggregation?: Array<{
      field: string
      type: string
    }>
    filtersArr?: FilterType[]
    where?: string
  }) => {
    if (!sharedView.value) return {}

    return await $api.public.dataTableAggregate(
      sharedView.value.uuid!,
      {
        ...param,
        filterArrJson: stringifyFilterOrSortArr(param.filtersArr ?? nestedFilters.value),
      } as any,
      {
        headers: {
          'xc-password': password.value,
        },
      },
    )
  }

  const fetchBulkAggregatedData = async (
    param: {
      aggregation?: Array<{
        field: string
        type: string
      }>
      filtersArr?: FilterType[]
      where?: string
    },
    bulkFilterList: Array<{
      where: string
      alias: string
    }>,
  ) => {
    if (!sharedView.value) return {}

    return await $api.public.dataTableBulkAggregate(
      sharedView.value.uuid!,
      bulkFilterList,
      {
        ...param,
        filterArrJson: stringifyFilterOrSortArr(param.filtersArr ?? nestedFilters.value),
      } as any,
      {
        headers: {
          'xc-password': password.value,
        },
      },
    )
  }

  const fetchBulkListData = async (
    param: {
      where?: string
    },
    bulkFilterList: Array<{
      where: string
      alias: string
    }>,
  ) => {
    if (!sharedView.value) return {}

    return await $api.public.dataTableBulkDataList(sharedView.value.uuid!, bulkFilterList, {
      ...param,
    } as any)
  }

  const fetchBulkGroupData = async (
    param: {
      filtersArr?: FilterType[]
      where?: string
    },
    bulkFilterList: Array<{
      where: string
      alias: string
    }>,
  ) => {
    if (!sharedView.value) return {}

    return await $api.public.dataTableBulkGroup(
      sharedView.value.uuid!,
      bulkFilterList,
      {
        ...param,
        filterArrJson: stringifyFilterOrSortArr(param.filtersArr ?? nestedFilters.value),
      } as any,
      {
        headers: {
          'xc-password': password.value,
        },
      },
    )
  }

  const fetchSharedViewActiveDate = async (param: {
    from_date: string
    to_date: string
    next_date: string
    prev_date: string
    sortsArr: SortType[]
    filtersArr: FilterType[]
    sort?: any[]
    where?: string
  }) => {
    if (!sharedView.value)
      return {
        list: [],
        pageInfo: {},
      }

    return await $api.public.dataCalendarRowCount(
      sharedView.value.uuid!,
      {
        ...param,
        filterArrJson: stringifyFilterOrSortArr(param.filtersArr ?? nestedFilters.value),
        sortArrJson: stringifyFilterOrSortArr(param.sortsArr ?? sorts.value),
      } as any,
      {
        headers: {
          'xc-password': password.value,
        },
      },
    )
  }

  const fetchCount = async (param: { filtersArr: FilterType[]; where?: string }) => {
    const data = await $api.public.dbViewRowCount(
      sharedView.value.uuid!,
      {
        filterArrJson: stringifyFilterOrSortArr(param.filtersArr ?? nestedFilters.value),
        where: param.where,
      },
      {
        headers: {
          'xc-password': password.value,
        },
      },
    )

    return data
  }

  const fetchSharedViewGroupedData = async (
    columnId: string,
    {
      sortsArr,
      filtersArr,
      include_row_color,
      where,
    }: { sortsArr: SortType[]; filtersArr: FilterType[]; include_row_color?: boolean; where?: string },
  ) => {
    if (!sharedView.value) return

    const page = paginationData.value.page || 1
    const pageSize = paginationData.value.pageSize || appInfoDefaultLimit

    return await $api.public.groupedDataList(
      sharedView.value.uuid!,
      columnId,
      {
        offset: (page - 1) * pageSize,
        filterArrJson: stringifyFilterOrSortArr(filtersArr ?? nestedFilters.value),
        sortArrJson: stringifyFilterOrSortArr(sortsArr ?? sorts.value),
        include_row_color,
        where,
      } as any,
      {
        headers: {
          'xc-password': password.value,
        },
      },
    )
  }

  const fetchSharedViewAttachment = async (columnId: string, rowId: string, urlOrPath: string) => {
    if (!sharedView.value) return

    return await $api.public.dataAttachmentDownload(
      sharedView.value.uuid!,
      columnId,
      rowId,
      {
        urlOrPath,
      } as any,
      {
        headers: {
          'xc-password': password.value,
        },
      },
    )
  }

  const exportFile = async (
    fields: any[],
    offset: number,
    type: ExportTypes.EXCEL | ExportTypes.CSV,
    responseType: 'base64' | 'blob',
    { sortsArr, filtersArr }: { sortsArr: SortType[]; filtersArr: FilterType[] } = {
      sortsArr: [],
      filtersArr: [],
    },
  ) => {
    return await $api.public.csvExport(sharedView.value!.uuid!, type, {
      format: responseType,
      query: {
        fields: fields.map((field) => field.title),
        offset,
        filterArrJson: stringifyFilterOrSortArr(filtersArr ?? nestedFilters.value),
        sortArrJson: stringifyFilterOrSortArr(sortsArr ?? sorts.value),
        encoding: type === ExportTypes.EXCEL ? 'base64' : undefined,
      },
      headers: {
        'xc-password': password.value,
      },
    } as RequestParams)
  }

  const triggerNotFound = () => {
    const isNcNotFoundQueryExists = router.currentRoute.value.query.ncNotFound === 'true'

    if (isNcNotFoundQueryExists) return

    const currentQuery = { ...router.currentRoute.value.query, ncNotFound: 'true' }

    router.push({
      path: router.currentRoute.value.path,
      query: currentQuery,
    })
  }

  return {
    sharedView,
    loadSharedView,
    meta,
    nestedFilters,
    fetchSharedViewData,
    fetchSharedViewActiveDate,
    fetchSharedCalendarViewData,
    fetchSharedViewGroupedData,
    fetchAggregatedData,
    fetchBulkAggregatedData,
    fetchSharedViewAttachment,
    fetchBulkGroupData,
    fetchBulkListData,
    paginationData,
    sorts,
    exportFile,
    formColumns,
    allowCSVDownload,
    fetchCount,
    triggerNotFound,
  }
}
