import { Property, OAuth2PropertyValue } from '@activepieces/pieces-framework';
import {
  getAccessTokenOrThrow,
  HttpMethod,
  HttpMessageBody,
  HttpResponse,
  httpClient,
  AuthenticationType,
} from '@activepieces/pieces-common';
import { ClickupTask, ClickupWorkspace } from './models';

export const clickupCommon = {
  workspace_id: (required = true) =>
    Property.Dropdown({
      description: 'The ID of the ClickUp workspace',
      displayName: 'Workspace',
      required,
      refreshers: [],
      options: async ({ auth }) => {
        if (!auth) {
          return {
            disabled: true,
            placeholder: 'connect your account first',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = (
          await callClickUpApi<{
            teams: {
              id: string;
              name: string;
            }[];
          }>(HttpMethod.GET, 'team', accessToken, undefined)
        ).body;
        return {
          disabled: false,
          options: response.teams.map((workspace) => {
            return {
              label: workspace.name,
              value: workspace.id,
            };
          }),
        };
      },
    }),
  space_id: (required = true, multi = false) => {
    const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown;
    return Dropdown({
      description: 'The ID of the ClickUp space to create the task in',
      displayName: 'Space',
      required,
      refreshers: ['workspace_id'],
      options: async ({ auth, workspace_id }) => {
        if (!auth || !workspace_id) {
          return {
            disabled: true,
            placeholder: 'connect your account first and select workspace',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = await listSpaces(accessToken, workspace_id as string);
        return {
          disabled: false,
          options: response.spaces.map((space) => {
            return {
              label: space.name,
              value: space.id,
            };
          }),
        };
      },
    });
  },
  list_id: (required = true, multi = false) => {
    const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown;
    return Dropdown({
      description: 'The ID of the ClickUp space to create the task in',
      displayName: 'List',
      required,
      refreshers: ['space_id', 'workspace_id', 'folder_id'],
      options: async ({ auth, space_id }) => {
        if (!auth || !space_id) {
          return {
            disabled: true,
            placeholder: 'connect your account first and select a space',
            options: [],
          };
        }

        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const lists: { name: string; id: string }[] = await listAllLists(
          accessToken,
          space_id as string
        );

        return {
          disabled: false,
          options: lists.map((list) => {
            return {
              label: list.name,
              value: list.id,
            };
          }),
        };
      },
    });
  },
  task_id: (required = true, label: string | undefined = undefined) =>
    Property.Dropdown({
      description: 'The ID of the ClickUp task',
      displayName: label ?? 'Task Id',
      required,
      defaultValue: null,
      refreshers: ['space_id', 'list_id', 'workspace_id', 'folder_id'],
      options: async ({ auth, space_id, list_id }) => {
        if (!auth || !list_id || !space_id) {
          return {
            disabled: true,
            placeholder:
              'connect your account first and select workspace, space and list',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = await listTasks(accessToken, list_id as string);
        return {
          disabled: false,
          options: response.tasks.map((task) => {
            return {
              label: task.name,
              value: task.id,
            };
          }),
        };
      },
    }),
  folder_id: (required = false, multi = false) => {
    const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown;
    return Dropdown({
      description: 'The ID of the ClickUp folder',
      displayName: 'Folder Id',
      refreshers: ['space_id', 'workspace_id'],
      required,
      options: async ({ auth, space_id, workspace_id }) => {
        if (!auth || !workspace_id || !space_id) {
          return {
            disabled: true,
            placeholder:
              'connect your account first and select workspace and space',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = await listFolders(accessToken, space_id as string);
        return {
          disabled: false,
          options: response.folders.map((task) => {
            return {
              label: task.name,
              value: task.id,
            };
          }),
        };
      },
    });
  },
  field_id: (required = false) =>
    Property.Dropdown({
      displayName: 'Field',
      description: 'The ID of the ClickUp custom field',
      refreshers: ['task_id', 'list_id'],
      defaultValue: null,
      required,
      options: async ({ auth, task_id, list_id }) => {
        if (!auth || !task_id || !list_id) {
          return {
            disabled: true,
            placeholder: 'connect your account first and select a task',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = await listAccessibleCustomFields(
          accessToken,
          list_id as string
        );
        return {
          disabled: false,
          options: response.fields.map((field) => {
            return {
              label: field.name,
              value: field.id,
            };
          }),
        };
      },
    }),
  status_id: (required = false, multi = false) => {
    const Dropdown = multi ? Property.MultiSelectDropdown : Property.Dropdown;
    return Dropdown({
      description: 'The ID of Clickup Issue Status',
      displayName: 'Status Id',
      refreshers: ['list_id'],
      required,
      options: async ({ auth, list_id }) => {
        if (!auth) {
          return {
            disabled: true,
            placeholder: 'Connect your account first',
            options: [],
          };
        }
        if (!list_id) {
          return {
            disabled: true,
            placeholder: 'select list',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = await getStatuses(accessToken, list_id as string);
        return {
          disabled: false,
          options: response.statuses.map((status) => {
            return {
              label: status.status,
              value: status.status,
            };
          }),
        };
      },
    });
  },
  priority_id: (required = false) =>
    Property.StaticDropdown({
      displayName: 'Priority Id',
      defaultValue: null,
      description: 'The ID of Clickup Issue Priority',
      required,
      options: {
        options: [
          {
            label: 'Urgent',
            value: 1,
          },
          {
            label: 'High',
            value: 2,
          },
          {
            label: 'Normal',
            value: 3,
          },
          {
            label: 'Low',
            value: 4,
          },
        ],
      },
    }),
  assignee_id: (
    required = false,
    displayName = 'Assignee Id',
    description: string
  ) =>
    Property.MultiSelectDropdown({
      displayName: displayName,
      description: description,
      required,
      refreshers: ['workspace_id'],
      options: async ({ auth, workspace_id }) => {
        if (!auth) {
          return {
            disabled: true,
            placeholder: 'connect your account first',
            options: [],
          };
        }
        if (!workspace_id) {
          return {
            disabled: true,
            placeholder: 'select workspace',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = await listWorkspaceMembers(
          accessToken,
          workspace_id as string
        );
        return {
          disabled: false,
          options: response.map((member) => {
            return {
              label: member.user.username,
              value: member.user.id,
            };
          }),
        };
      },
    }),
  single_assignee_id: (
    required = false,
    displayName = 'Assignee Id',
    description: string
  ) =>
    Property.Dropdown({
      displayName: displayName,
      description: description,
      required,
      refreshers: ['workspace_id'],
      options: async ({ auth, workspace_id }) => {
        if (!auth) {
          return {
            disabled: true,
            placeholder: 'connect your account first',
            options: [],
          };
        }
        if (!workspace_id) {
          return {
            disabled: true,
            placeholder: 'select workspace',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = await listWorkspaceMembers(
          accessToken,
          workspace_id as string
        );
        return {
          disabled: false,
          options: response.map((member) => {
            return {
              label: member.user.username,
              value: member.user.id,
            };
          }),
        };
      },
    }),
  template_id: (required = false) =>
    Property.Dropdown({
      displayName: 'Template Id',
      required,
      description: 'The ID of Clickup Task Template',
      refreshers: ['workspace_id'],
      options: async ({ auth, workspace_id }) => {
        if (!auth) {
          return {
            disabled: true,
            placeholder: 'connect your account first',
            options: [],
          };
        }
        if (!workspace_id) {
          return {
            disabled: true,
            placeholder: 'select workspace',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = await listWorkspaceTemplates(
          accessToken,
          workspace_id as string
        );
        return {
          disabled: false,
          options: response.templates.map((template) => {
            return {
              label: template.name,
              value: template.id,
            };
          }),
        };
      },
    }),
  channel_id: (required = false) =>
    Property.Dropdown({
      displayName: 'Channel Id',
      required,
      description: 'The ID of Clickup Channel',
      refreshers: ['workspace_id'],
      options: async ({ auth, workspace_id }) => {
        if (!auth) {
          return {
            disabled: true,
            placeholder: 'connect your account first',
            options: [],
          };
        }
        if (!workspace_id) {
          return {
            disabled: true,
            placeholder: 'select workspace',
            options: [],
          };
        }
        const accessToken = getAccessTokenOrThrow(auth as OAuth2PropertyValue);
        const response = await retrieveChannels(
          accessToken,
          workspace_id as string
        );
        return {
          disabled: false,
          options: response.data
            .filter((channel) => channel.name && channel.name !== '')
            .map((channel) => {
              return {
                label: channel.name,
                value: channel.id,
              };
            }),
        };
      },
    }),
};

async function listWorkspaces(accessToken: string) {
  return (
    await callClickUpApi<{ teams: ClickupWorkspace[] }>(
      HttpMethod.GET,
      'team',
      accessToken,
      undefined
    )
  ).body;
}

async function getWorkspace(accessToken: string, workspaceId: string) {
  const { teams } = await listWorkspaces(accessToken as string);
  const workspace = teams.filter((workspace) => workspace.id === workspaceId);
  return workspace[0];
}

async function listWorkspaceMembers(accessToken: string, workspaceId: string) {
  const workspace = await getWorkspace(accessToken, workspaceId);
  return workspace.members;
}

async function listWorkspaceTemplates(
  accessToken: string,
  workspaceId: string
) {
  return (
    await callClickUpApi<{
      templates: {
        id: string;
        name: string;
      }[];
    }>(
      HttpMethod.GET,
      `team/${workspaceId}/taskTemplate`,
      accessToken,
      undefined
    )
  ).body;
}

export async function listSpaces(accessToken: string, workspaceId: string) {
  return (
    await callClickUpApi<{
      spaces: {
        id: string;
        name: string;
      }[];
    }>(HttpMethod.GET, `team/${workspaceId}/space`, accessToken, undefined)
  ).body;
}

export async function listAllLists(accessToken: string, spaceId: string) {
  const responseFolders = await listFolders(accessToken, spaceId as string);
  const promises: Promise<{ lists: { id: string; name: string }[] }>[] = [
    listFolderlessList(accessToken, spaceId as string),
  ];
  for (let i = 0; i < responseFolders.folders.length; ++i) {
    promises.push(listLists(accessToken, responseFolders.folders[i].id));
  }
  const listsResponses = await Promise.all(promises);

  let lists: { name: string; id: string }[] = [];
  for (let i = 0; i < listsResponses.length; ++i) {
    lists = [...lists, ...listsResponses[i].lists];
  }

  return lists;
}

export async function listLists(accessToken: string, folderId: string) {
  return (
    await callClickUpApi<{
      lists: {
        id: string;
        name: string;
      }[];
    }>(HttpMethod.GET, `folder/${folderId}/list`, accessToken, undefined)
  ).body;
}
async function getStatuses(accessToken: string, listId: string) {
  return (
    await callClickUpApi<{
      statuses: {
        id: string;
        status: string;
      }[];
    }>(HttpMethod.GET, `list/${listId}`, accessToken, undefined)
  ).body;
}

export async function listFolders(accessToken: string, spaceId: string) {
  return (
    await callClickUpApi<{
      folders: {
        id: string;
        name: string;
      }[];
    }>(HttpMethod.GET, `space/${spaceId}/folder`, accessToken, undefined)
  ).body;
}

export async function listAccessibleCustomFields(
  accessToken: string,
  listId: string
) {
  return (
    await callClickUpApi<{
      fields: {
        id: string;
        name: string;
        type: string;
        type_config: Record<string, unknown>;
        date_created: string;
        hide_from_guests: false;
      }[];
    }>(HttpMethod.GET, `list/${listId}/field`, accessToken, undefined)
  ).body;
}

async function listFolderlessList(accessToken: string, spaceId: string) {
  return (
    await callClickUpApi<{
      lists: {
        id: string;
        name: string;
      }[];
    }>(HttpMethod.GET, `space/${spaceId}/list`, accessToken, undefined)
  ).body;
}

export async function listTags(accessToken: string, spaceId: string) {
  return (
    await callClickUpApi<{
      tags: {
        id: string;
        name: string;
      }[];
    }>(HttpMethod.GET, `space/${spaceId}/tag`, accessToken, undefined)
  ).body;
}

export async function listTasks(accessToken: string, listId: string) {
  return (
    await callClickUpApi<{
      tasks: {
        id: string;
        name: string;
      }[];
    }>(HttpMethod.GET, `list/${listId}/task`, accessToken, undefined)
  ).body;
}

export async function retrieveChannels(
  accessToken: string,
  workspaceId: string
) {
  return (
    await callClickUpApi3<{
      data: {
        id: string;
        name: string;
      }[];
    }>(
      HttpMethod.GET,
      `workspaces/${workspaceId}/chat/channels`,
      accessToken,
      undefined
    )
  ).body;
}

export async function callClickupGetTask(accessToken: string, taskId: string) {
  return (
    await callClickUpApi<ClickupTask>(
      HttpMethod.GET,
      `task/${taskId}`,
      accessToken,
      undefined
    )
  ).body;
}

export async function callClickUpApi<T extends HttpMessageBody = any>(
  method: HttpMethod,
  apiUrl: string,
  accessToken: string,
  body: any | undefined,
  queryParams: any | undefined = undefined,
  headers: any | undefined = undefined
): Promise<HttpResponse<T>> {
  headers = {
    accept: 'application/json',
    ...headers,
  };

  return await httpClient.sendRequest<T>({
    method: method,
    url: `https://api.clickup.com/api/v2/${apiUrl}`,
    authentication: {
      type: AuthenticationType.BEARER_TOKEN,
      token: accessToken,
    },
    headers,
    body: method === 'GET' ? undefined : body,
    queryParams,
  });
}

export async function callClickUpApi3<T extends HttpMessageBody = any>(
  method: HttpMethod,
  apiUrl: string,
  accessToken: string,
  body: any | undefined,
  queryParams: any | undefined = undefined,
  headers: any | undefined = {}
): Promise<HttpResponse<T>> {
  headers = {
    accept: 'application/json',
    ...headers,
  };

  return await httpClient.sendRequest<T>({
    method: method,
    url: `https://api.clickup.com/api/v3/${apiUrl}`,
    authentication: {
      type: AuthenticationType.BEARER_TOKEN,
      token: accessToken,
    },
    headers,
    body: method === 'GET' ? undefined : body,
    queryParams,
  });
}
