import packageJson from '../../../package.json';

const GITHUB_BUG_REPORT_URL = 'https://github.com/yancyuu/Hermit/issues/new';
const MAX_TITLE_LENGTH = 120;
const URL_MAX_STACK_LENGTH = 1800;
const URL_MAX_COMPONENT_STACK_LENGTH = 1200;
const COPY_MAX_STACK_LENGTH = 12000;
const COPY_MAX_COMPONENT_STACK_LENGTH = 8000;

export interface BugReportContext {
  activeTabType?: string | null;
  activeTabLabel?: string | null;
  activeTeamName?: string | null;
  selectedTeamName?: string | null;
  taskId?: string | null;
  sessionId?: string | null;
  projectId?: string | null;
}

export interface BugReportOptions {
  error: Error | null;
  componentStack?: string | null;
  context?: BugReportContext;
}

const truncate = (value: string, maxLength: number): string => {
  if (value.length <= maxLength) {
    return value;
  }

  return `${value.slice(0, maxLength)}\n...[truncated]`;
};

const buildIssueTitle = (error: Error | null): string => {
  const baseTitle = error ? `[BUG] ${error.name}: ${error.message}` : '[BUG] Application crash';
  return truncate(baseTitle, MAX_TITLE_LENGTH);
};

const getRuntimeLabel = (): string => 'Web browser';

const formatOptional = (value: string | null | undefined): string => {
  if (!value) {
    return 'Not available';
  }

  const trimmed = value.trim();
  return trimmed.length > 0 ? trimmed : 'Not available';
};

const getOperatingSystemLabel = (): string => {
  const { userAgent } = window.navigator;

  if (userAgent.includes('Mac OS X')) return 'macOS';
  if (userAgent.includes('Windows')) return 'Windows';
  if (userAgent.includes('Linux')) return 'Linux';

  return 'Unknown';
};

const formatActiveTab = (context?: BugReportContext): string => {
  if (!context?.activeTabType) {
    return 'Not available';
  }

  if (!context.activeTabLabel) {
    return context.activeTabType;
  }

  return `${context.activeTabType} (${context.activeTabLabel})`;
};

const buildBugReportMarkdown = (
  { error, componentStack, context }: BugReportOptions,
  stackLimits: { js: number; react: number }
): string => {
  const message = error?.message ?? 'Unknown application crash';
  const jsStack = error?.stack ? truncate(error.stack, stackLimits.js) : 'Not available';
  const reactComponentStack = componentStack
    ? truncate(componentStack, stackLimits.react)
    : 'Not available';

  return [
    '**Describe the bug**',
    'The app crashed and showed the global error screen.',
    '',
    '**What happened**',
    `- Error: \`${message}\``,
    `- Error type: \`${error?.name ?? 'UnknownError'}\``,
    `- Active tab: ${formatActiveTab(context)}`,
    `- Active team tab: ${formatOptional(context?.activeTeamName)}`,
    `- Selected team: ${formatOptional(context?.selectedTeamName)}`,
    `- Current task: ${formatOptional(context?.taskId)}`,
    `- Session ID: ${formatOptional(context?.sessionId)}`,
    `- Project ID: ${formatOptional(context?.projectId)}`,
    '',
    '**Steps to reproduce**',
    '1. Open the app and navigate to the screen where the crash happened.',
    '2. Repeat the action that triggered the error.',
    '3. Observe the global error screen.',
    '',
    '**Expected behavior**',
    'The app should continue working instead of crashing.',
    '',
    '**Screenshots**',
    'Attach a screenshot if you have one.',
    '',
    '**Environment**',
    `- OS: ${getOperatingSystemLabel()}`,
    `- Runtime: ${getRuntimeLabel()}`,
    `- App version: ${packageJson.version}`,
    '',
    '**Diagnostics**',
    '```text',
    `Timestamp: ${new Date().toISOString()}`,
    `Current URL: ${window.location.href}`,
    `User Agent: ${window.navigator.userAgent}`,
    `Error name: ${error?.name ?? 'UnknownError'}`,
    `Error message: ${message}`,
    `Active tab: ${formatActiveTab(context)}`,
    `Active team tab: ${formatOptional(context?.activeTeamName)}`,
    `Selected team: ${formatOptional(context?.selectedTeamName)}`,
    `Current task: ${formatOptional(context?.taskId)}`,
    `Session ID: ${formatOptional(context?.sessionId)}`,
    `Project ID: ${formatOptional(context?.projectId)}`,
    '```',
    '',
    '**JavaScript stack trace**',
    '```text',
    jsStack,
    '```',
    '',
    '**React component stack**',
    '```text',
    reactComponentStack,
    '```',
  ].join('\n');
};

export const buildBugReportText = (options: BugReportOptions): string =>
  buildBugReportMarkdown(options, {
    js: COPY_MAX_STACK_LENGTH,
    react: COPY_MAX_COMPONENT_STACK_LENGTH,
  });

export const buildGitHubBugReportUrl = (options: BugReportOptions): string => {
  const params = new URLSearchParams({
    template: 'bug_report.md',
    labels: 'bug',
    title: buildIssueTitle(options.error),
    body: buildBugReportMarkdown(options, {
      js: URL_MAX_STACK_LENGTH,
      react: URL_MAX_COMPONENT_STACK_LENGTH,
    }),
  });

  return `${GITHUB_BUG_REPORT_URL}?${params.toString()}`;
};
