import inquirer from "inquirer";
import { systemApiKeyService } from "../service/ordino.systemApiKey.service";
import { userApiKeyService } from "../service/ordino.userApiKey.service";
import { printMessage } from "../utils/printMessage.util";
import { userService } from "../service/ordino.user.service";
import { teamService } from "../service/ordino.team.service";
import { projectService } from "../service/ordino.project.service";
import {
  User,
  Team,
  Project,
  projectTypes,
  ApplicationType,
} from "../models/models";
import { serviceFactory } from "../config/service-factory";
import { getApiEndpoints } from "../config/api-endpoints";

// Types for the prompts
type SystemLoginAnswers = {
  username: string;
  password: string;
};

type ProjectAnswer = {
  project: Project
};

type InitialPromptAnswers = {
  isCompanyIDAvailable: boolean;
};

export type ProjectTypeAnswers = {
  authToken: string;
  projectType: projectTypes;
};

type MicroFrontendAnswers = {
  clientTestProjectURL: string;
  testProjectURL: string;
};

type TestPromptAnswers = {
  includeSampleTest: boolean;
};

type GitUrlAndYmlAnswers = {
  gitUrl: string | null;
  needGitActionSetup: boolean;
};

// Functions

// Initial prompt to ask if the company ID is available
async function getInitialPrompts(): Promise<InitialPromptAnswers> {
  return inquirer.prompt([
    {
      type: "confirm",
      name: "isCompanyIDAvailable",
      message: "Do you have a company ID?",
      default: true,
    },
  ]);
}

// Validate the provided API key
async function validateApiKey(apiKey: string): Promise<true | string> {
  const frameworkVersion = "1.0.0";

  if (apiKey.trim() === "") {
    return "API key is required!!!";
  }

  try {
    // Get the current environment and corresponding endpoints
    const currentEnvironment = serviceFactory.getEnvironment();
    const endpoints = getApiEndpoints(currentEnvironment);

    const response = await fetch(endpoints.authentication, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ apiKey, frameworkVersion }),
    });

    if (!response.ok) {
      return "Use a valid key.";
    }

    const data = await response.json();

    switch (data.status) {
      case "INVALID":
        return "Use a valid key.";
      case "FRAMEWORK_OUT_DATED":
        return "Update your CLI.";
      case "VALID":
        return true;
      default:
        return "Unexpected response. Please try again.";
    }
  } catch (error) {
    return `Use a valid key. ${error}`;
  }
}

// Prompt for project type and company ID
async function projectType(): Promise<ProjectTypeAnswers> {
  return inquirer.prompt([
    {
      type: "input",
      name: "authToken",
      message: "Provide the Company ID:",
      validate: validateApiKey,
    },
    {
      type: "list",
      name: "projectType",
      message: "What is the type of your project? (Choose one: UI / API):",
      choices: mapEnumToChoices(),
    },
  ]);
}

// Get prompts for Micro Frontend project URLs
async function getMicroFrontendPrompts(): Promise<MicroFrontendAnswers> {
  return inquirer.prompt([
    {
      type: "input",
      name: "clientTestProjectURL",
      message: "Please add the Client Test Project URL:",
    },
    {
      type: "input",
      name: "testProjectURL",
      message: "Please add the Test Project URL:",
    },
  ]);
}

// Get prompts for project name and company
async function getStandardPrompts(
  projectName: string,
  companyName: string
): Promise<string> {
  const suggestedName = `${companyName}-${projectName}-test`;
  const nameConfirmation = await inquirer.prompt<{ useSuggestedName: boolean }>(
    [
      {
        type: "confirm",
        name: "useSuggestedName",
        message: `Would you like the tool to suggest a project name (${suggestedName})?`,
        default: true,
      },
    ]
  );

  if (!nameConfirmation.useSuggestedName) {
    const customName = await inquirer.prompt<{ customProjectName: string }>([
      {
        type: "input",
        name: "customProjectName",
        message: "Please enter a project name:",
        validate: (input: string) =>
          input.trim() !== "" || "Project name cannot be empty.",
      },
    ]);
    return customName.customProjectName;
  }
  return suggestedName;
}

// Get project details based on API key and project type
async function getProjectName(
  apiKey: string,
  projectType: projectTypes
): Promise<Project | null> {
  const partnerId = await getPartnerId(apiKey);
  if (!partnerId) {
    return null;
  }

  const users: User[] = await userService.getAllUsers(partnerId);
  const teams: Team[] = await teamService.getAllTeams(partnerId);
  const projects: Project[] = await projectService.getAllProjects();

  if (projects.length > 0) {
    const { projectId } = await inquirer.prompt({
      type: "list",
      name: "projectId",
      message: "Select a project:",
      choices: projects.map((project) => ({
        name: project.name,
        value: project.id,
      })),
    });
    return projects.find(({ id }) => id === projectId) ?? null;
  } else if (teams.length > 0) {
    const { teamId } = await inquirer.prompt({
      type: "list",
      name: "teamId",
      message: "Select a team:",
      choices: teams.map((team) => ({ name: team.name, value: team.id })),
    });

    const { projectName } = await inquirer.prompt([
      {
        type: "input",
        name: "projectName",
        message: "Enter the project name:",
      },
    ]);

    const { userId } = await inquirer.prompt({
      type: "list",
      name: "userId",
      message: "Select a user:",
      choices: users.map((user) => ({
        name: user.information.email,
        value: user.id,
      })),
    });

    const project = await projectService.cerateProject({
      partnerId: partnerId,
      teamId: teamId,
      name: projectName,
      applicationTypes: getApplicationType(projectType),
      qaLeadId: userId,
      isRegressionSuite: false,
      isCloudEnabled: false,
    });

    return project ?? null;
  } else {
    const { teamName, projectName } = await inquirer.prompt([
      {
        type: "input",
        name: "teamName",
        message: "Enter the team name:",
      },
      {
        type: "input",
        name: "projectName",
        message: "Enter the project name:",
      },
    ]);

    const team = await teamService.createTeam({
      partnerId: partnerId,
      name: teamName,
      description: "",
      applicationTypes: getApplicationType(projectType),
      integrationDefinitionIds: ["TEST_INTEGRATION"],
    });

    const { userId } = await inquirer.prompt({
      type: "list",
      name: "userId",
      message: "Select a user:",
      choices: users.map((user) => ({
        name: user.information.email,
        value: user.id,
      })),
    });

    const project = await projectService.cerateProject({
      partnerId: partnerId,
      teamId: team.id,
      name: projectName,
      applicationTypes: getApplicationType(projectType),
      qaLeadId: userId,
      isRegressionSuite: false,
      isCloudEnabled: false,
    });

    return project ?? null;
  }
}

// Prompt for whether to include a sample test
async function getTestPrompt(): Promise<TestPromptAnswers> {
  return inquirer.prompt([
    {
      type: "confirm",
      name: "includeSampleTest",
      message: "Do you want to add a sample test? (yes/no):",
      default: true,
    },
  ]);
}

// Map project types to choices for user selection
function mapEnumToChoices(): { name: string; value: projectTypes }[] {
  return Object.keys(projectTypes)
    .filter((key) => isNaN(Number(key)))
    .map((key) => ({
      name: key,
      value: projectTypes[key as keyof typeof projectTypes],
    }));
}

// Get application types based on project type
function getApplicationType(projectType: projectTypes) {
  switch (projectType) {
    case projectTypes.UI:
      return [ApplicationType.WEB_UI];
    case projectTypes.API:
      return [ApplicationType.API];
    case projectTypes.UI_AND_API:
      return [ApplicationType.WEB_UI, ApplicationType.API];
    default:
      throw new Error("Invalid project type");
  }
}

// Get the partner ID based on the API key
async function getPartnerId(apiKey: string): Promise<string | null> {
  const partnerId: string | null = null;
  try {
    if (apiKey.startsWith("SYS:")) {
      const res = await systemApiKeyService.getBySystemApiKey(apiKey);
      return res.partnerId;
    }
    if (apiKey.startsWith("USR:")) {
      const res = await userApiKeyService.getByUserApiKey(apiKey);
      return res.partnerId;
    }
  } catch (error) {
    printMessage(
      `Please check you api key is valid. If it's valid please contact support team https://www.ordino.ai/support`,
      "35",
      true,
      true
    );
    return partnerId;
  }

  return partnerId;
}

// Convert project name to a valid package name
export function convertToValidPackageName(projectName: string) {
  const validName = projectName
    .toLowerCase()
    .replace(/\s+/g, "-")
    .replace(/[^a-z0-9-]/g, "")
    .replace(/^-+/, "")
    .replace(/-+$/, "");

  return validName;
}

async function handleGitAndYml(): Promise<GitUrlAndYmlAnswers> {
  const gitUrlResponse = await inquirer.prompt([
    {
      type: "confirm",
      name: "gitUrl",
      message: "Do you want to set up a Git repository?",
      default: true,
    },
  ]);

  let gitUrl = null;
  let needGitActionSetup = false;

  if (gitUrlResponse.gitUrl) {
    const gitUrlInput = await inquirer.prompt([
      {
        type: "input",
        name: "gitUrl",
        message: "Please provide the Git repository URL:",
        validate: (input: string) =>
          input.trim() !== "" ? true : "Git URL cannot be empty.",
      },
      {
        type: "confirm",
        name: "needGitActionSetup",
        message: "Do you want to use GitHub Actions for CI/CD?",
        default: true,
      },
    ]);
    gitUrl = gitUrlInput.gitUrl;
    needGitActionSetup = gitUrlInput.needGitActionSetup;
  }

  return { gitUrl, needGitActionSetup };
}

//Initial prompt to ask login credentials
async function getLoginPrompts(): Promise<SystemLoginAnswers> {
  return inquirer.prompt<SystemLoginAnswers>([
    {
      type: "input",
      name: "username",
      message: "Ordino portal login username?",
      validate: (input: string) =>
        input.trim() !== "" ? true : "Username cannot be empty.",
    },
    {
      type: "password",
      name: "password",
      message: "Ordino portal login password?",
      validate: (input: string) =>
        input.trim() !== "" ? true : "password cannot be empty.",
    },
  ]);
}

//Get projects list
async function getProjectsList(projectsList: Project[]): Promise<ProjectAnswer> {
  return inquirer.prompt([
    {
      type: "list",
      name: "project",
      message: "Please select the project?",
      choices: mapProjectsList(projectsList),
    }
  ]);
}

//Map projects list.
function mapProjectsList(projectsList: Project[]): { name: string, value: Project }[] {
  return projectsList.map((project) => ({
      name: project.name,
      value: project
    }));
}

export {
  getLoginPrompts,
  getProjectsList,
  getInitialPrompts,
  getMicroFrontendPrompts,
  getStandardPrompts,
  getTestPrompt,
  projectType,
  getProjectName,
  handleGitAndYml,
};
