import { useCallback, useEffect, useMemo } from 'react';
import {
  AxiosInterceptorManager,
  AxiosRequestConfig,
  AxiosResponse,
  isAxiosError,
  isCancel,
  ValidationError,
} from '@szyx-mobile/use-axios';
import {
  RequestError,
  RequestOptions,
  useRequest,
} from '@szyx-mobile/use-request';
import { z } from 'zod';
import { Platform } from 'react-native';
import { useAuth } from '@common/contexts/useAuth.ts';
import { useCommon } from '@common/contexts/useCommon.ts';
import { getEnvUrl } from '@common/env/envUtils.ts';
import { md5_hex } from '@common/utils/md5';
import { CLIENT_ID, MD5_KEY } from '@common/constants';
import { showErrorMessage } from '@common/ToastHelper.ts';

const SUCCESS_STATUS = /^0$/; // 请求成功的状态码

type Response<T> = {
  status: string;
  message: string;
  data: T;
};

const useApi = <
  S extends z.ZodTypeAny,
  T extends z.infer<S> = unknown,
  D = unknown,
>(
  url: string,
  method: 'GET' | 'POSTJSON' | 'POSTFORM' | 'PUT' | 'UPLOAD',
  params?: D,
  validationSchema?: S,
  options?: RequestOptions,
  config?: AxiosRequestConfig<D>,
): {
  response?: T;
  error?: RequestError<unknown, unknown>;
  loading: boolean;
  fetch: (config?: AxiosRequestConfig<D>) => void;
  fetchAsync: (config?: AxiosRequestConfig<D>) => Promise<T>;
  cancel: () => void;
  requestInterceptors: AxiosInterceptorManager<AxiosRequestConfig<unknown>>;
  responseInterceptors: AxiosInterceptorManager<
    AxiosResponse<Response<T>, unknown>
  >;
} => {
  const {
    state: { token, userInfo },
    actions: { logout },
  } = useAuth();

  const {
    state: { info },
  } = useCommon();

  // 【这里增加公共参数】
  const commonParams = useMemo(() => {
    return {};
  }, []);

  // 【这里添加额外的 headers】
  const commonHeaders = useMemo(() => {
    try {
      const deviceInfo = JSON.parse(info.deviceInfo ?? '{}') as Record<
        string,
        string
      >;

      return {
        clientId: CLIENT_ID, // 厂商唯一标识，这个值是为医网信app分配的
        deviceType: Platform.select({ android: '0', ios: '1' }),
        version: deviceInfo.versionName,
        deviceId: deviceInfo.deviceId,
        phoneModel: deviceInfo.phoneModel,
        phoneBrand: deviceInfo.phoneBrand,
        phoneVersion: deviceInfo.phoneVersion,

        userId: userInfo?.userId ?? '',
        sessionId: token ?? '',
        currentClientId: '',
        appType: '0', // 医网信是 0，冠新是 1
      };
    } catch {
      return {};
    }
  }, [info.deviceInfo, token, userInfo?.userId]);

  // 【这里定义 Response 的 schema】
  const responseSchema = useMemo(() => {
    // 这里 code 和 message 字段为项目接口中相应的字段名和字段类型
    return z.object({
      status: z.string().regex(SUCCESS_STATUS),
      message: z.string(),
      data: validationSchema ?? z.unknown(),
    });
  }, [validationSchema]);

  const {
    response,
    error,
    loading,
    fetch,
    fetchAsync,
    cancel,
    requestInterceptors,
    responseInterceptors,
  } = useRequest<typeof responseSchema, Response<T>, unknown>(
    url,
    method,
    { ...params, ...commonParams },
    responseSchema,
    options,
    {
      // 【这里配置 baseURL】
      baseURL: getEnvUrl(info.env ?? 'production').url,
      ...config,
      timeout: 30000,
      headers: {
        ...commonHeaders,
        ...config?.headers,
      },
    },
  );

  useEffect(() => {
    const requestInterceptor = requestInterceptors.use(c => {
      // 这几个header的参数因为是实时的，所以单独拿出来
      const timeStamp = Date.now();
      const signOrigin = `timeStamp=${timeStamp}#${MD5_KEY}`;
      const sign = md5_hex(signOrigin);
      c.headers = {
        ...c.headers,
        timeStamp: timeStamp + '',
        sign: sign,
      };
      return c;
    });
    return () => {
      requestInterceptors.eject(requestInterceptor);
    };
  }, [requestInterceptors]);

  useEffect(() => {
    const responseInterceptor = responseInterceptors.use(
      r => {
        if (options?.log) {
          console.debug(JSON.stringify(r, null, 2));
        }
        return r;
      },
      e => {
        if (isCancel(e)) {
          return Promise.reject(
            new RequestError('网络请求已取消', 'Cancel', e),
          );
        }

        if (e instanceof ValidationError) {
          console.error(
            JSON.stringify(
              {
                name: e.name,
                status: e.response.status,
                data: e.response.data,
                config: e.response.config,
                headers: e.response.headers,
                issues: e.issues,
                url: `${e.response.config.baseURL}${e.response.config.url}`,
              },
              null,
              2,
            ),
          );
          // 退出登录逻辑
          if (e.response.data?.status === '017x025'||e.response.data?.status === '017x013') {
            showErrorMessage(
              e.response.data?.message ??
                '该账号已在其他设备登录，请重新登录！',
            );
            logout();

            return Promise.reject(
              new RequestError(
                e.response.data?.message ??
                  '该账号已在其他设备登录，请重新登录！',
                'ValidationError',
                e,
              ),
            );
          }

          if (e.issues.length <= 0) {
            return Promise.reject(
              new RequestError('数据格式校验错误', 'ValidationError', e),
            );
          }
          // 这里 code 为项目接口中相应的字段名
          if (e.issues[0].path.length > 0 && e.issues[0].path[0] === 'status') {
            return Promise.reject(
              new RequestError(
                e.response.data?.message ?? '', // 这里 message 为项目接口中相应的字段名
                'ValidationError',
                e,
              ),
            );
          }
          return Promise.reject(
            new RequestError(e.issues[0].message, 'ValidationError', e),
          );
        }

        if (isAxiosError(e)) {
          console.error(
            JSON.stringify(e, null, 2),
            `${e.config?.baseURL}${e.config?.url}`,
          );
          return Promise.reject(
            new RequestError('网络请求失败', 'AxiosError', e),
          );
        }

        console.error(
          JSON.stringify(e, null, 2),
          `${e.config?.baseURL}${e.config?.url}`,
        );
        return Promise.reject(
          new RequestError('网络请求失败', 'OtherError', e),
        );
      },
    );
    return () => {
      responseInterceptors.eject(responseInterceptor);
    };
  }, [logout, options?.log, options?.tag, responseInterceptors]);

  const transformedFetchAsync = useCallback(
    async (c?: AxiosRequestConfig<D>) => {
      try {
        const r = await fetchAsync(c);
        return r.data.data;
      } catch (e) {
        return Promise.reject(e);
      }
    },
    [fetchAsync],
  );

  return {
    response: response?.data.data,
    error: error as RequestError<unknown, unknown>,
    loading,
    fetch,
    fetchAsync: transformedFetchAsync,
    cancel,
    requestInterceptors,
    responseInterceptors,
  };
};

export { useApi };
