/**
 * Git remote URL utilities — GitHub-focused.
 *
 * Parsing, validation, and compare-URL generation for git remote URLs.
 * Used by both the UI and the vite-plugin-file-sync AI agent tooling.
 */

const HTTPS_GIT_URL_RE = /^https?:\/\/[^\s/$.?#]+(?:\.[^\s/$.?#]+)+\/[^\s/$.?#]+(\/[^\s/$.?#]+)+(\.git)?\/?$/i;
const SSH_GIT_URL_RE = /^[\w-]+@[\w.-]+:[\w./-]+(\.git)?$/;
// ssh:// protocol format: ssh://git@host/path
const SSH_PROTOCOL_RE = /^ssh:\/\/[\w-]+@[\w.-]+(\/[\w./-]+)+(\.git)?\/?$/i;

export const GITHUB_PAT_SETTINGS_URL = 'https://github.com/settings/tokens';

export function isValidGitUrl(url: string): boolean {
  const trimmed = url.trim();
  return HTTPS_GIT_URL_RE.test(trimmed) || SSH_GIT_URL_RE.test(trimmed) || SSH_PROTOCOL_RE.test(trimmed);
}

export function isGitHubUrl(url: string): boolean {
  const trimmed = url.trim();

  try {
    const parsed = new URL(trimmed);
    if (parsed.hostname.toLowerCase() === 'github.com') return true;
  } catch {
    // Not a valid URL — try SSH format
  }

  const sshMatch = trimmed.match(/^[\w-]+@([\w.-]+):/);
  if (sshMatch?.[1]?.toLowerCase() === 'github.com') return true;

  return false;
}

/**
 * Converts a git remote URL (HTTPS or SSH) to a browser-friendly HTTPS URL.
 * Returns `null` when the URL cannot be converted.
 *
 *   https://github.com/org/repo.git  →  https://github.com/org/repo
 *   git@github.com:org/repo.git      →  https://github.com/org/repo
 */
export function gitRemoteUrlToWeb(url: string): string | null {
  const trimmed = url.trim();

  try {
    const parsed = new URL(trimmed);
    if (parsed.protocol === 'https:' || parsed.protocol === 'http:') {
      // Strip userinfo (e.g. "org@" in Azure DevOps clone URLs) so the
      // result is a plain browser-friendly URL.
      parsed.username = '';
      parsed.password = '';
      return parsed.href.replace(/\/+$/, '').replace(/\.git\s*$/, '');
    }
    // ssh:// protocol format: ssh://git@host/path
    if (parsed.protocol === 'ssh:') {
      const host = parsed.hostname;
      const path = parsed.pathname.replace(/\.git\s*$/, '');
      // Azure DevOps SSH: ssh://git@ssh.dev.azure.com/v3/org/project/repo
      if (host === 'ssh.dev.azure.com') {
        const azureMatch = path.match(/^\/v3\/([^/]+)\/([^/]+)\/(.+?)$/);
        if (azureMatch) {
          return `https://dev.azure.com/${azureMatch[1]}/${azureMatch[2]}/_git/${azureMatch[3]}`;
        }
      }
      // Generic ssh:// → https:// (GitHub, GitLab, etc.)
      return `https://${host}${path}`;
    }
  } catch {
    // Not a standard URL — try SSH
  }

  // SCP-style SSH: git@host:org/repo.git → https://host/org/repo
  const sshMatch = trimmed.match(/^[\w-]+@([\w.-]+):(.+?)(?:\.git)?\s*$/);
  if (sshMatch?.[1] && sshMatch[2]) {
    const host = sshMatch[1];
    const path = sshMatch[2];
    // Azure DevOps SSH: git@ssh.dev.azure.com:v3/org/project/repo → https://dev.azure.com/org/project/_git/repo
    if (host === 'ssh.dev.azure.com') {
      const azureMatch = path.match(/^v3\/([^/]+)\/([^/]+)\/(.+)$/);
      if (azureMatch) {
        return `https://dev.azure.com/${azureMatch[1]}/${azureMatch[2]}/_git/${azureMatch[3]}`;
      }
    }
    return `https://${host}/${path}`;
  }

  return null;
}

/**
 * Returns a URL that opens the "new pull request" compare screen on GitHub.
 * Returns `null` when the remote URL is not a GitHub URL or cannot be parsed.
 */
export function getGitHubCompareUrl({
  gitRemoteUrl,
  fromBranch,
  toBranch,
  title,
  body
}: {
  gitRemoteUrl: string;
  fromBranch: string;
  toBranch: string;
  title?: string;
  body?: string;
}): string | null {
  if (!isGitHubUrl(gitRemoteUrl)) return null;

  const webUrl = gitRemoteUrlToWeb(gitRemoteUrl);
  if (!webUrl) return null;

  const base = `${webUrl}/compare/${encodeURIComponent(toBranch)}...${encodeURIComponent(fromBranch)}`;
  if (!title && !body) return base;

  const params = new URLSearchParams();
  params.set('expand', '1');
  if (title) params.set('title', title);
  if (body) params.set('body', body);
  return `${base}?${params.toString()}`;
}

export type GitProvider = 'github' | 'gitlab' | 'bitbucket' | 'azure_devops' | 'unknown';

export const HOST_TO_PROVIDER: Record<string, GitProvider> = {
  'bitbucket.org': 'bitbucket',
  'dev.azure.com': 'azure_devops',
  'github.com': 'github',
  'gitlab.com': 'gitlab',
  'ssh.dev.azure.com': 'azure_devops'
};

export function detectGitProvider(url: string): GitProvider {
  const trimmed = url.trim();

  try {
    const parsed = new URL(trimmed);
    return HOST_TO_PROVIDER[parsed.hostname.toLowerCase()] ?? 'unknown';
  } catch {
    // Not a standard URL — try SSH format.
  }

  const sshMatch = trimmed.match(/^[\w-]+@([\w.-]+):/);
  if (sshMatch?.[1]) {
    return HOST_TO_PROVIDER[sshMatch[1].toLowerCase()] ?? 'unknown';
  }

  return 'unknown';
}

function getCommitPathForProvider(params: { hash: string; provider: GitProvider }): string {
  const { hash, provider } = params;
  switch (provider) {
    case 'bitbucket':
      return `/commits/${hash}`;
    case 'gitlab':
      return `/-/commit/${hash}`;
    case 'azure_devops':
    case 'github':
    case 'unknown':
      return `/commit/${hash}`;
  }
}

const GIT_COMMIT_HASH_REGEX = /^[0-9a-f]{7,40}$/i;

export function isGitCommitHash(value: string): boolean {
  return GIT_COMMIT_HASH_REGEX.test(value.trim());
}

export function getCommitUrlForHash(params: { hash: string; repositoryWebUrl: string }): string | null {
  const { hash, repositoryWebUrl } = params;
  const trimmedHash = hash.trim();
  if (!isGitCommitHash(trimmedHash)) {
    return null;
  }

  const trimmedRepositoryUrl = repositoryWebUrl.trim();
  const webUrl = gitRemoteUrlToWeb(trimmedRepositoryUrl) ?? trimmedRepositoryUrl;
  const provider = detectGitProvider(trimmedRepositoryUrl);

  return `${webUrl.replace(/\/+$/, '')}${getCommitPathForProvider({
    hash: trimmedHash,
    provider
  })}`;
}

/**
 * Converts an SSH git remote URL to its HTTPS equivalent suitable for
 * `git clone` / `git fetch` with a credential helper.  HTTPS URLs are
 * returned as-is (with `.git` suffix ensured, except for Azure DevOps which
 * uses `/_git/repo` format without `.git`).  Returns `null` when the URL
 * cannot be converted.
 *
 *   git@github.com:org/repo.git               → https://github.com/org/repo.git
 *   git@gitlab-self.example.com:root/app.git   → https://gitlab-self.example.com/root/app.git
 *   ssh://git@github.com/org/repo              → https://github.com/org/repo.git
 *   https://github.com/org/repo.git            → https://github.com/org/repo.git (passthrough)
 *   https://org@dev.azure.com/org/proj/_git/repo → https://org@dev.azure.com/org/proj/_git/repo (no .git)
 */
export function sshGitUrlToHttps(url: string): string | null {
  const trimmed = url.trim();

  try {
    const parsed = new URL(trimmed);
    if (parsed.protocol === 'https:' || parsed.protocol === 'http:') {
      // Azure DevOps HTTPS URLs use /_git/repo format without .git suffix
      if (parsed.hostname === 'dev.azure.com' && parsed.pathname.includes('/_git/')) {
        return trimmed.replace(/\/+$/, '');
      }
      return ensureGitSuffix(trimmed);
    }
    if (parsed.protocol === 'ssh:') {
      const host = parsed.hostname;
      const path = parsed.pathname;
      if (host === 'ssh.dev.azure.com') {
        const azureMatch = path.match(/^\/v3\/([^/]+)\/([^/]+)\/(.+?)(?:\.git)?\/?$/);
        if (azureMatch) {
          return `https://dev.azure.com/${azureMatch[1]}/${azureMatch[2]}/_git/${azureMatch[3]}`;
        }
      }
      return ensureGitSuffix(`https://${host}${path}`);
    }
  } catch {
    // Not a standard URL — try SCP-style SSH below.
  }

  const sshMatch = trimmed.match(/^[\w-]+@([\w.-]+):(.+?)(?:\.git)?\s*$/);
  if (sshMatch?.[1] && sshMatch[2]) {
    const host = sshMatch[1];
    const path = sshMatch[2];
    if (host === 'ssh.dev.azure.com') {
      const azureMatch = path.match(/^v3\/([^/]+)\/([^/]+)\/(.+)$/);
      if (azureMatch) {
        return `https://dev.azure.com/${azureMatch[1]}/${azureMatch[2]}/_git/${azureMatch[3]}`;
      }
    }
    return `https://${host}/${path}.git`;
  }

  return null;
}

function ensureGitSuffix(url: string): string {
  const cleaned = url.replace(/\/+$/, '');
  return cleaned.endsWith('.git') ? cleaned : `${cleaned}.git`;
}

export function isCommitUrlForHash(params: { hash: string; url: string }): boolean {
  const { hash, url } = params;
  const trimmedHash = hash.trim();
  const trimmedUrl = url.trim();
  if (!isGitCommitHash(trimmedHash)) {
    return false;
  }

  try {
    const parsed = new URL(trimmedUrl);
    const normalizedPathname = parsed.pathname.replace(/\/+$/, '');
    return normalizedPathname.endsWith(
      getCommitPathForProvider({
        hash: trimmedHash,
        provider: detectGitProvider(trimmedUrl)
      })
    );
  } catch {
    return false;
  }
}
