package org.bndtools.templating.jgit.ui;

import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.bndtools.templating.jgit.Cache;
import org.bndtools.templating.jgit.GitHub;
import org.bndtools.templating.jgit.GitHubValidationException;
import org.bndtools.templating.jgit.GithubRepoDetailsDTO;
import org.bndtools.utils.jface.ProgressRunner;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import aQute.bnd.header.Attrs;
import aQute.libg.tuple.Pair;

public class GitHubRepoDialog extends AbstractNewEntryDialog {

	private final Cache				cache		= new Cache();
	private final String			title;
	private final ExecutorService	executor;

	private String					repository	= null;
	private String					branch		= null;
	private Text					txtRepository;
	private Text					txtBranch;
	private volatile boolean		isValidated	= false;

	public GitHubRepoDialog(Shell parentShell, String title) {
		super(parentShell);
		this.title = title;
		setShellStyle(getShellStyle() | SWT.RESIZE);
		this.executor = Executors.newCachedThreadPool();
	}

	@Override
	protected Control createDialogArea(Composite parent) {
		setTitle(title);
		Composite area = (Composite) super.createDialogArea(parent);
		Composite container = new Composite(area, SWT.NONE);
		container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

		container.setLayout(new GridLayout(2, false));

		Label lblRepo = new Label(container, SWT.NONE);
		lblRepo.setText("Repository Name:");

		txtRepository = new Text(container, SWT.BORDER);
		txtRepository.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		txtRepository.setMessage("username/repository");
		if (repository != null)
			txtRepository.setText(repository);

		new Label(container, SWT.NONE).setText("Branch:");
		txtBranch = new Text(container, SWT.BORDER);
		txtBranch.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		txtBranch.setMessage("default branch");
		if (branch != null)
			txtBranch.setText(branch);

		ControlDecoration branchDecor = new ControlDecoration(txtBranch, SWT.LEFT, container);
		branchDecor.setDescriptionText(
			"Specify the branch, tag or commit ID you would like to clone from the\nrepository. We use the default branch specified in GitHub settings.");
		branchDecor.setImage(FieldDecorationRegistry.getDefault()
			.getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION)
			.getImage());
		branchDecor.setShowHover(true);

		final Button btnValidate = new Button(container, SWT.PUSH);
		btnValidate.setText("Validate");
		btnValidate.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1));
		ModifyListener modifyListener = ev -> {
			if (!txtRepository.getText()
				.trim()
				.equals(repository)) {
				isValidated = false;
				repository = txtRepository.getText()
					.trim();
			}
			branch = txtBranch.getText()
				.trim();
			updateButtons();
		};
		txtRepository.addModifyListener(modifyListener);
		txtBranch.addModifyListener(modifyListener);

		btnValidate.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				setMessage(null, IMessageProvider.INFORMATION);
				try {
					if (repository == null || repository.isEmpty())
						throw new GitHubValidationException("No repository name specified");

					if (repository.contains(":/"))
						throw new GitHubValidationException("Specify GitHub repositories as username/repository");

					IRunnableWithProgress runnable = monitor -> {
						try {
							final GithubRepoDetailsDTO dto = new GitHub(cache, executor).loadRepoDetails(repository)
								.getValue();
							final URI cloneUri = URI.create(dto.clone_url);
							btnValidate.getDisplay()
								.asyncExec(() -> {
									setMessage(String.format("Validated! Clone URL is '%s'. Default branch 'origin/%s'",
										cloneUri, dto.default_branch), IMessageProvider.INFORMATION);
									isValidated = true;
									updateButtons();
								});
						} catch (InvocationTargetException e1) {
							throw e1;
						} catch (Exception e2) {
							throw new InvocationTargetException(e2);
						}
					};

					ProgressRunner.execute(false, runnable, new ProgressMonitorDialog(getParentShell()),
						btnValidate.getDisplay());
					setErrorMessage(null);
				} catch (InvocationTargetException ex) {
					Throwable t = ex.getCause();
					if (t instanceof FileNotFoundException)
						setErrorMessage("Could not find the requested repository");
					else
						setErrorMessage(t.getClass()
							.getSimpleName() + ": " + t.getMessage());
				} catch (GitHubValidationException ex) {
					setErrorMessage(ex.getMessage());
				} catch (Exception ex) {
					setErrorMessage(ex.getClass()
						.getSimpleName() + ": " + ex.getMessage());
				}
			}
		});

		return area;
	}

	@Override
	protected void createButtonsForButtonBar(Composite parent) {
		super.createButtonsForButtonBar(parent);

		Button ok = getButton(OK);
		ok.setText("Save");
		ok.setEnabled(repository != null);
	}

	private void updateButtons() {
		getButton(OK).setEnabled(repository != null && !repository.trim()
			.isEmpty() && isValidated);
	}

	@Override
	public void setEntry(Pair<String, Attrs> entry) {
		repository = entry.getFirst();
		Attrs attrs = entry.getSecond();
		branch = attrs.get("branch");
		if (txtRepository != null && !txtRepository.isDisposed())
			txtRepository.setText(repository);
		if (txtBranch != null && !txtBranch.isDisposed())
			txtBranch.setText(branch);
		isValidated = true;
	}

	@Override
	public Pair<String, Attrs> getEntry() {
		Attrs attrs = new Attrs();
		if (branch != null && !branch.trim()
			.isEmpty())
			attrs.put("branch", branch);
		return repository != null ? new Pair<>(repository.trim(), attrs) : null;
	}

	@Override
	public boolean close() {
		executor.shutdown();
		return super.close();
	}
}
