/*
 * SonarQube
 * Copyright (C) 2009-2024 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
import * as React from 'react';
import { getGitlabProjects } from '../../../../api/alm-integrations';
import { Location, Router } from '../../../../components/hoc/withRouter';
import { GitlabProject } from '../../../../types/alm-integration';
import { AlmSettingsInstance } from '../../../../types/alm-settings';
import { Paging } from '../../../../types/types';
import { ImportProjectParam } from '../CreateProjectPage';
import { CreateProjectModes } from '../types';
import GitlabProjectCreateRenderer from './GitlabProjectCreateRenderer';

interface Props {
  canAdmin: boolean;
  loadingBindings: boolean;
  almInstances: AlmSettingsInstance[];
  location: Location;
  router: Router;
  onProjectSetupDone: (importProjects: ImportProjectParam) => void;
}

interface State {
  loading: boolean;
  loadingMore: boolean;
  projects?: GitlabProject[];
  projectsPaging: Paging;
  resetPat: boolean;
  searching: boolean;
  searchQuery: string;
  selectedAlmInstance: AlmSettingsInstance;
  showPersonalAccessTokenForm: boolean;
}

const GITLAB_PROJECTS_PAGESIZE = 20;

export default class GitlabProjectCreate extends React.PureComponent<Props, State> {
  mounted = false;

  constructor(props: Props) {
    super(props);

    this.state = {
      loading: false,
      loadingMore: false,
      projectsPaging: { pageIndex: 1, total: 0, pageSize: GITLAB_PROJECTS_PAGESIZE },
      resetPat: false,
      showPersonalAccessTokenForm: true,
      searching: false,
      searchQuery: '',
      selectedAlmInstance: props.almInstances[0],
    };
  }

  componentDidMount() {
    this.mounted = true;
  }

  componentDidUpdate(prevProps: Props) {
    const { almInstances } = this.props;
    if (prevProps.almInstances.length === 0 && this.props.almInstances.length > 0) {
      this.setState({ selectedAlmInstance: almInstances[0] }, () => {
        this.fetchInitialData().catch(() => {
          /* noop */
        });
      });
    }
  }

  componentWillUnmount() {
    this.mounted = false;
  }

  fetchInitialData = async () => {
    const { showPersonalAccessTokenForm } = this.state;

    if (!showPersonalAccessTokenForm) {
      this.setState({ loading: true });
      const result = await this.fetchProjects();
      if (this.mounted && result) {
        const { projects, projectsPaging } = result;

        this.setState({
          loading: false,
          projects,
          projectsPaging,
        });
      } else {
        this.setState({
          loading: false,
        });
      }
    }
  };

  handleError = () => {
    if (this.mounted) {
      this.setState({ resetPat: true, showPersonalAccessTokenForm: true });
    }

    return undefined;
  };

  fetchProjects = async (pageIndex = 1, query?: string) => {
    const { selectedAlmInstance } = this.state;
    if (!selectedAlmInstance) {
      return Promise.resolve(undefined);
    }

    try {
      // eslint-disable-next-line local-rules/no-api-imports
      return await getGitlabProjects({
        almSetting: selectedAlmInstance.key,
        page: pageIndex,
        pageSize: GITLAB_PROJECTS_PAGESIZE,
        query,
      });
    } catch (_) {
      return this.handleError();
    }
  };

  handleImport = (gitlabProjectId: string) => {
    const { selectedAlmInstance } = this.state;

    if (selectedAlmInstance) {
      this.props.onProjectSetupDone({
        creationMode: CreateProjectModes.GitLab,
        almSetting: selectedAlmInstance.key,
        monorepo: false,
        projects: [{ gitlabProjectId }],
      });
    }
  };

  handleLoadMore = async () => {
    this.setState({ loadingMore: true });

    const {
      projectsPaging: { pageIndex },
      searchQuery,
    } = this.state;

    const result = await this.fetchProjects(pageIndex + 1, searchQuery);
    if (this.mounted) {
      this.setState(({ projects = [], projectsPaging }) => ({
        loadingMore: false,
        projects: result ? [...projects, ...result.projects] : projects,
        projectsPaging: result ? result.projectsPaging : projectsPaging,
      }));
    }
  };

  handleSearch = async (searchQuery: string) => {
    this.setState({ searching: true, searchQuery });

    const result = await this.fetchProjects(1, searchQuery);
    if (this.mounted) {
      this.setState(({ projects, projectsPaging }) => ({
        searching: false,
        projects: result ? result.projects : projects,
        projectsPaging: result ? result.projectsPaging : projectsPaging,
      }));
    }
  };

  cleanUrl = () => {
    const { location, router } = this.props;
    delete location.query.resetPat;
    router.replace(location);
  };

  handlePersonalAccessTokenCreated = () => {
    this.cleanUrl();
    this.setState({ showPersonalAccessTokenForm: false, resetPat: false }, () => {
      this.fetchInitialData();
    });
  };

  onSelectedAlmInstanceChange = (instance: AlmSettingsInstance) => {
    this.setState({
      selectedAlmInstance: instance,
      showPersonalAccessTokenForm: true,
      projects: undefined,
      resetPat: false,
      searchQuery: '',
    });
  };

  render() {
    const { loadingBindings, location, almInstances, canAdmin } = this.props;
    const {
      loading,
      loadingMore,
      projects,
      projectsPaging,
      resetPat,
      searching,
      searchQuery,
      selectedAlmInstance,
      showPersonalAccessTokenForm,
    } = this.state;

    return (
      <GitlabProjectCreateRenderer
        canAdmin={canAdmin}
        almInstances={almInstances}
        selectedAlmInstance={selectedAlmInstance}
        loading={loading || loadingBindings}
        loadingMore={loadingMore}
        onImport={this.handleImport}
        onLoadMore={this.handleLoadMore}
        onPersonalAccessTokenCreated={this.handlePersonalAccessTokenCreated}
        onSearch={this.handleSearch}
        projects={projects}
        projectsPaging={projectsPaging}
        resetPat={resetPat || Boolean(location.query.resetPat)}
        searching={searching}
        searchQuery={searchQuery}
        showPersonalAccessTokenForm={
          showPersonalAccessTokenForm || Boolean(location.query.resetPat)
        }
        onSelectedAlmInstanceChange={this.onSelectedAlmInstanceChange}
      />
    );
  }
}
