import dayjs from 'dayjs';
import { kebabCase } from 'lodash-es';

import { useUserInfoStore } from '@/stores/user-info';
import { t } from '@/i18n';
import { fileContentTypeRegExp, TimeScaleType } from '@/constants';

import { removeToken } from './auth';
import { fetchWithTimeout } from './fetch';

import type { SorterResult } from 'ant-design-vue/es/table/interface';
import type { GlobalToken } from 'ant-design-vue/es/theme';
import type { Dayjs } from 'dayjs';
import type { ApiResponse, PageSorts } from '@/types';

export const request = async <T>(url: string, init: RequestInit = {}, timeout?: number): Promise<T> => {
  const headers: HeadersInit = {};

  if (typeof init?.body === 'string') {
    headers['Content-Type'] = 'application/json;charset=UTF-8';
  }
  const { token, resetToken } = useUserInfoStore();

  if (token) {
    Object.assign(headers, {
      Authorization: 'Bearer ' + token,
    });
  }

  Object.assign(headers, init?.headers);

  const res = await fetchWithTimeout(
    import.meta.env.VITE_BASE_API + url,
    {
      ...init,
      headers,
    },
    timeout,
  );

  if (res.status === 401) {
    removeToken();
    resetToken();
    location.replace('/login');
    throw new Error(t('common.reLogin'));
  }

  if (!res.ok || res.status !== 200) {
    const json = await res.json();
    const errMsg: string = json.msg || json.error || '';
    throw new Error(`HTTP Error: ${res.status} ${errMsg}`);
  }

  const contentType = res.headers.get('content-type');
  const isBlob = fileContentTypeRegExp.test(contentType || '');

  if (isBlob) {
    return (await res.blob()) as T;
  }

  const json: ApiResponse<T> = await res.json();
  const { data, code, msg } = json;

  if (code !== 0) {
    throw new Error(msg);
  }

  if (data === null || data === undefined) {
    throw new Error(t('common.emptyData'));
  }

  return data;
};

export const translateNavigation = (title?: string) => {
  return title ? t('navigation.' + title) : '';
};

/**
 * 根据 Antd 当前主题的 token 生成 css var
 */
export const generateThemeCSSVar = (token: GlobalToken, prefix: string): string => {
  const cssVar = Object.entries(token)
    .filter(([key]) => {
      return /^(color|line|link|motion|border|margin|font(?!fam)|padding(?!con)|screen((?!min|max).)*$|boxShadow(?!pop|dra|tab|car))/i.test(
        key,
      );
    })
    .map(([key, value]) => {
      return `--${prefix}-${kebabCase(key)}:${value}${/(size|width|radius|margin|screen|padding)/gi.test(key) ? 'px' : ''}`;
    });

  cssVar.push(...getColorGradientCSSVar(token.colorPrimary, `${prefix}-color-primary`));

  return `:root{${cssVar.join(';')}}`;
};

/**
 * 根据十六进制值颜色生成透明度为 5%～95% 对应颜色的 css var
 */
export const getColorGradientCSSVar = (color: string, prefix: string): string[] => {
  const rgb = hexToRgb(color);

  if (!rgb) {
    return [];
  }

  const cssVar: string[] = [];
  const { r, g, b } = rgb;

  cssVar.push(`--${prefix}-rgb:${r}, ${g}, ${b}`);

  for (let i = 5; i <= 95; i += 5) {
    cssVar.push(`--${prefix}-opacity-${i}:rgba(${r}, ${g}, ${b}, ${i / 100})`);
  }

  return cssVar;
};

export const hexToRgb = (hex: string): { r: number; g: number; b: number } | null => {
  const cleanedHex = hex.replace(/^#/, '');

  if (cleanedHex.length !== 3 && cleanedHex.length !== 6) {
    console.error('Invalid hex color value');
    return null;
  }

  let extendedHex = cleanedHex;

  // 将 3 位十六进制扩展为 6 位（例如 #abc -> #aabbcc）
  if (cleanedHex.length === 3) {
    extendedHex = cleanedHex
      .split('')
      .map((char) => char + char)
      .join('');
  }

  const r = parseInt(extendedHex.slice(0, 2), 16);
  const g = parseInt(extendedHex.slice(2, 4), 16);
  const b = parseInt(extendedHex.slice(4, 6), 16);

  return { r, g, b };
};

export const addUnit = (val: number, unit: string = 'px'): string => {
  return val + unit;
};

export const waitTime = async (time: number = 100) => {
  await new Promise((resolve) => {
    setTimeout(resolve, time);
  });
};

export const downloadBlob = (blob: Blob, name: string) => {
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement('a');

  a.href = url;
  a.download = name;

  document.body.appendChild(a);
  a.click();

  document.body.removeChild(a);
  window.URL.revokeObjectURL(url);
};

/**
 * 根据表格排序配置生成分页查询时的字段排序数组
 * @param sorter 表格进行排序的字段数组
 */
export const getTablePageSorts = (sorter: SorterResult | SorterResult[]): PageSorts[] => {
  let sorts: SorterResult[];

  if (Array.isArray(sorter)) {
    sorts = sorter;
  } else {
    sorts = [sorter];
  }

  sorts = sorts.filter((item) => item.field !== undefined && item.order !== undefined);

  if (sorts.length) {
    return sorts.map<PageSorts>((item) => ({
      column: item.field as string,
      asc: item.order === 'ascend',
    }));
  }

  return [
    {
      column: 'id',
      asc: true,
    },
  ];
};

export const formatTimeByScale = (time: string, scale?: TimeScaleType) => {
  switch (scale) {
    case TimeScaleType.Hour:
      return dayjs(time).format('HH:mm');
    case TimeScaleType.Day:
      return dayjs(time).format('MM-DD');
    case TimeScaleType.Month:
      return dayjs(time).format('YYYY-MM');
    default:
      return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
  }
};

export const timeSorter = (a: string, b: string) => dayjs(a).unix() - dayjs(b).unix();

export const getEChartsColors = (count: number) => {
  switch (count) {
    case 0:
      return [];
    case 1:
      return ['#32BAC0'];
    case 2:
      return ['#32BAC0', '#5B8FF9'];
    case 3:
      return ['#32BAC0', '#5B8FF9', '#5D7092'];
    case 4:
      return ['#32BAC0', '#5B8FF9', '#5D7092', '#F6BD16'];
    case 5:
      return ['#32BAC0', '#5B8FF9', '#5D7092', '#F6BD16', '#E86452'];
    case 6:
      return ['#32BAC0', '#5B8FF9', '#5D7092', '#F6BD16', '#FF9845', '#E86452'];
    case 7:
      return ['#32BAC0', '#5B8FF9', '#6DC8EC', '#5D7092', '#F6BD16', '#FF9845', '#E86452'];
    case 8:
      return ['#32BAC0', '#5B8FF9', '#6DC8EC', '#5D7092', '#F3DD13', '#F6BD16', '#FF9845', '#E86452'];
    case 9:
      return ['#32BAC0', '#5B8FF9', '#6DC8EC', '#5D7092', '#7E93B2', '#F3DD13', '#F6BD16', '#FF9845', '#E86452'];
    default:
      return [
        '#32BAC0',
        '#5AD8A6',
        '#5B8FF9',
        '#6DC8EC',
        '#5D7092',
        '#7E93B2',
        '#F3DD13',
        '#F6BD16',
        '#FF9845',
        '#E86452',
      ];
  }
};

export const calcPercentage = (currentValue?: number, maxValue?: number) => {
  const percentage = ((currentValue || 0) / (maxValue || 100)) * 100;
  return Number(percentage.toFixed(2));
};

export const isEmptyVal = (value: unknown) => {
  return value === undefined || value === null;
};

export const isNotEmptyVal = (value: unknown) => {
  return value !== undefined && value !== null;
};

export const disabledDate = (current: Dayjs) => {
  return current > dayjs().endOf('day');
};

export const getFixedNum = (value: number | string | null | undefined, decimalPlaces: number = 2) => {
  if (value === undefined || value === null || value === '') {
    return '-';
  }

  const num = Number(value);

  if (isNaN(num)) {
    return '-';
  }

  return num.toFixed(decimalPlaces);
};
