import { ListItem as UserListItem } from '@/pages/users/config/types';
import { downloadFile, listFiles, listModels } from '@huggingface/hub';
import { PipelineType } from '@huggingface/tasks';
import { request } from '@umijs/max';
import qs from 'query-string';
import {
  AccessControlFormData,
  BackendItem,
  CatalogItem,
  CatalogSpec,
  DraftModelItem,
  EvaluateResult,
  EvaluateSpec,
  FormData,
  GPUListItem,
  ListItem,
  ModelInstanceFormData,
  ModelInstanceListItem
} from '../config/types';

export const MODELS_API = '/models';

export const MODEL_INSTANCE_API = '/model-instances';

export const MODEL_EVALUATIONS = '/model-evaluations';

export const BACKEND_LIST_API = '/inference-backends/list';

export const MY_MODELS_API = '/my-models';

export const DRAFT_MODELS_API = '/draft-models';

export const CATALOG_LIST_API = '/model-sets';

const setProxyUrl = (url: string) => {
  return `/proxy?url=${encodeURIComponent(url)}`;
};

// ===================== Models =====================

export async function queryModelsInstances(
  params: Global.SearchParams,
  options?: any
) {
  return request<Global.PageResponse<ModelInstanceListItem>>(
    MODEL_INSTANCE_API,
    {
      params,
      method: 'GET',
      cancelToken: options?.token
    }
  );
}
export async function queryModelsList(
  params: Global.SearchParams,
  options?: Record<string, any>
) {
  return request<Global.PageResponse<ListItem>>(
    `${MODELS_API}?${qs.stringify(params)}`,
    {
      method: 'GET',
      ...options
    }
  );
}

export async function queryGPUList<T extends Record<string, any>>(
  params?: Global.SearchParams & T
) {
  return request<Global.PageResponse<GPUListItem>>(`/gpu-devices`, {
    method: 'GET',
    params
  });
}

export async function createModel(params: { data: FormData }) {
  return request(`${MODELS_API}`, {
    method: 'POST',
    data: params.data
  });
}

export async function deleteModel(id: number) {
  return request(`${MODELS_API}/${id}`, {
    method: 'DELETE'
  });
}

export async function updateModel(params: { id: number; data: FormData }) {
  return request(`${MODELS_API}/${params.id}`, {
    method: 'PUT',
    data: params.data
  });
}

export async function queryModelDetail(id: number) {
  return request(`${MODELS_API}/${id}`, {
    method: 'GET'
  });
}

// ===================== Model Instances start =====================

export async function queryModelInstancesList(
  params: Global.Pagination & { query?: string; id: number },
  options?: any
) {
  return request<Global.PageResponse<ModelInstanceListItem>>(
    `${MODELS_API}/${params.id}/instances`,
    {
      method: 'GET',
      params,
      cancelToken: options?.token
    }
  );
}

export async function createModelInstance(params: {
  data: ModelInstanceFormData;
}) {
  return request(`${MODEL_INSTANCE_API}`, {
    method: 'POST',
    data: params.data
  });
}

export async function deleteModelInstance(id: number) {
  return request(`${MODEL_INSTANCE_API}/${id}`, {
    method: 'DELETE'
  });
}

export async function updateModelInstance(params: {
  id: number;
  data: FormData;
}) {
  return request(`${MODEL_INSTANCE_API}/${params.id}`, {
    method: 'PUT',
    data: params.data
  });
}

export async function queryModelInstanceDetail(id: number) {
  return request(`${MODEL_INSTANCE_API}/${id}`, {
    method: 'GET'
  });
}

export async function queryModelInstanceLogs(id: number) {
  return request(`${MODEL_INSTANCE_API}/${id}/logs`, {
    method: 'GET'
  });
}

// ===================== Model Instances end =====================

// ===================== call huggingface quicksearch api =====================

const MODEL_SCOPE_LIST_MODEL_API = `https://www.modelscope.cn/api/v1/dolphin/models`;

const MODE_SCOPE_MODEL_FIELS_API = `https://modelscope.cn/api/v1/models/`;

export async function queryHuggingfaceModelDetail(
  params: { repo: string },
  options?: any
) {
  const url = `https://huggingface.co/api/models/${params.repo}`;
  return request(setProxyUrl(url), {
    method: 'GET',
    cancelToken: options?.token
  });
}

export async function queryModelScopeModels(
  params: {
    PageSize?: number;
    PageNumber?: number;
    SortBy?: string;
    Target?: string;
    SingleCriterion?: any[];
    Name: string;
    tags?: string[];
    tasks?: string[];
  },
  config?: any
) {
  const tagsCriterion = params.tags?.map((tag: string) => {
    return { category: 'tags', predicate: 'contains', values: [tag] };
  });
  const tasksCriterion = params.tasks?.map((task: string) => {
    return { category: 'tasks', predicate: 'contains', values: [task] };
  });

  const Criterion =
    tagsCriterion?.length || tasksCriterion?.length
      ? {
          Criterion: [...(tagsCriterion || []), ...(tasksCriterion || [])]
        }
      : {};

  const res = await fetch(setProxyUrl(`${MODEL_SCOPE_LIST_MODEL_API}`), {
    method: 'PUT',
    signal: config?.signal,
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      PageSize: 10,
      PageNumber: 1,
      ...params,
      ...Criterion,
      Name: `${params.Name}`
    })
  });
  if (!res.ok) {
    throw new Error('Network response was not ok');
  }
  return res.json();
}

export async function queryModelScopeModelDetail(
  params: { name: string },
  options?: any
) {
  return request(setProxyUrl(`${MODE_SCOPE_MODEL_FIELS_API}${params.name}`), {
    method: 'GET',
    cancelToken: options?.token
  });
}

export async function queryModelScopeModelFiles(
  params: { name: string; revision: string },
  options?: any
) {
  const url = `${MODE_SCOPE_MODEL_FIELS_API}${params.name}/repo/files?${qs.stringify(
    {
      Revision: params.revision,
      Recursive: true,
      Root: ''
    }
  )}`;
  const res = await fetch(setProxyUrl(url), {
    method: 'GET',
    signal: options?.signal,
    body: null
  });

  if (!res.ok) {
    throw new Error('Network response was not ok');
  }

  return res.json();
}

// list models from huggingface
export async function queryHuggingfaceModels(
  params: {
    limit?: number;
    search: {
      query: string;
      tags?: string[];
      sort?: string;
      task?: PipelineType;
    };
  },
  options?: any
) {
  console.log('params', params);
  const result = [];
  for await (const model of listModels({
    ...params,
    ...options,
    limit: params.limit || 500,
    additionalFields: ['sha', 'tags'],
    fetch(_url: string, config: any) {
      const url = params.search.sort
        ? `${_url}&sort=${params.search.sort}`
        : _url;
      try {
        return fetch(setProxyUrl(url), {
          ...config,
          signal: options.signal
        });
      } catch (error) {
        // ignore
        return [];
      }
    }
  })) {
    result.push(model);
  }
  return result;
}

// list files from huggingface
export async function queryHuggingfaceModelFiles(
  params: { repo: string },
  options?: any
) {
  const result = [];
  for await (const fileInfo of listFiles({
    ...params,
    recursive: true,
    fetch(url: string, config: any) {
      try {
        return fetch(setProxyUrl(url), {
          ...config,
          signal: options?.signal
        });
      } catch (error) {
        // ignore
        return [];
      }
    }
  })) {
    result.push(fileInfo);
  }
  return result;
}

export async function downloadModelFile(
  params: { repo: string; revision: string; path: string },
  options?: any
) {
  const { repo, revision, path } = params;
  const res = await (
    await downloadFile({
      repo,
      revision: revision,
      path: path,
      fetch(url: string, config: any) {
        return fetch(setProxyUrl(url), {
          ...config,
          signal: options?.signal
        });
      }
    })
  )?.text();
  return res;
}
export async function downloadModelScopeModelfile(
  params: { name: string },
  options?: any
) {
  const url = `${MODE_SCOPE_MODEL_FIELS_API}${params.name}/resolve/master/config.json`;
  const res = await fetch(setProxyUrl(url), {
    method: 'GET',
    signal: options?.signal
  });
  if (!res.ok) {
    throw new Error('Network response was not ok');
  }
  return res.json();
}

// ===================== catalog =====================

export async function queryCatalogList(
  params: Global.SearchParams,
  options?: any
) {
  return request<Global.PageResponse<CatalogItem>>(`${CATALOG_LIST_API}`, {
    method: 'GET',
    params,
    cancelToken: options?.token
  });
}

export async function queryCatalogItemSpec(
  params: { id: number; cluster_id: number | null },
  options?: any
) {
  return request<Global.PageResponse<CatalogSpec>>(
    `${CATALOG_LIST_API}/${params.id}/specs`,
    {
      method: 'GET',
      ...options,
      params
    }
  );
}

export async function evaluationsModelSpec(
  data: {
    cluster_id: number;
    model_specs: EvaluateSpec[];
  },
  options: { token: any }
) {
  const result = await request<{ results: EvaluateResult[] }>(
    `${MODEL_EVALUATIONS}`,
    {
      method: 'POST',
      data,
      cancelToken: options?.token
    }
  );

  const resultList = result?.results || [];

  return {
    results: resultList.map((item) => {
      return {
        ...item,
        cluster_id: data.model_specs?.[0]?.cluster_id || undefined
      };
    })
  };
}

export async function queryBackendList(params?: { cluster_id: number }) {
  return request<{
    items: BackendItem[];
  }>(BACKEND_LIST_API, {
    method: 'GET',
    params
  });
}

export async function queryModelAccessUserList(id: number) {
  return request<{ items: UserListItem[] }>(`${MODELS_API}/${id}/access`, {
    method: 'GET'
  });
}

export async function updateModelAccessUser(params: {
  id: number;
  data: AccessControlFormData;
}) {
  return request(`${MODELS_API}/${params.id}/access`, {
    method: 'POST',
    data: params.data
  });
}

export async function queryMyModels(params: Global.SearchParams) {
  return request<Global.PageResponse<ListItem>>(
    `${MY_MODELS_API}?${qs.stringify(params)}`,
    {
      method: 'GET'
    }
  );
}

export async function queryMyModelDetail(id: number) {
  return request(`${MY_MODELS_API}/${id}`, {
    method: 'GET'
  });
}

export async function queryDraftModelList(params?: Global.SearchParams) {
  return request<{ items: DraftModelItem[] }>(DRAFT_MODELS_API, {
    method: 'GET',
    params
  });
}

export async function queryModelContextLength(params: {
  model: {
    source: string;
    model_scope_model_id?: string;
    huggingface_repo_id?: string;
    local_path?: string;
  };
}) {
  return request<{ native: number; scaled: number }>(
    `${MODELS_API}/context-length`,
    {
      method: 'POST',
      data: params
    }
  );
}
