import { NextRequest, NextResponse } from 'next/server';
import { getSession } from '@/lib/auth';
import { dbHelpers } from '@/lib/db';

/**
 * Fetches comprehensive repository information from GitHub public API.
 * Query params: repoUrl (full GitHub URL), branch (optional, default 'main')
 *
 * Returns: { repo, tree, languages, commits, contributors, readme }
 */

function parseGitHubUrl(url: string): { owner: string; repo: string } | null {
  // https://github.com/owner/repo or github.com/owner/repo
  const match = url.match(/github\.com\/([^/]+)\/([^/.\s]+)/);
  if (!match) return null;
  return { owner: match[1], repo: match[2] };
}

async function ghFetch(path: string, token?: string, required = false): Promise<any> {
  const headers: Record<string, string> = {
    Accept: 'application/vnd.github.v3+json',
    'User-Agent': 'Scriba-AI',
  };
  if (token) headers.Authorization = `Bearer ${token}`;

  // No caching — avoid serving stale rate-limit failures
  const res = await fetch(`https://api.github.com${path}`, { headers, cache: 'no-store' });
  if (!res.ok) {
    if (res.status === 403 || res.status === 429) throw new Error('rate_limit');
    // Only treat 404 as a hard error for required endpoints (repo itself, tree)
    if (res.status === 404 && required) throw new Error('not_found');
    return null;
  }
  return res.json();
}

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const repoUrl = searchParams.get('repoUrl');
  const branch = searchParams.get('branch') || 'main';

  if (!repoUrl) {
    return NextResponse.json({ error: 'repoUrl is required' }, { status: 400 });
  }

  const parsed = parseGitHubUrl(repoUrl);
  if (!parsed) {
    return NextResponse.json({ error: 'Invalid GitHub URL' }, { status: 400 });
  }

  const { owner, repo } = parsed;

  // Prefer user's stored PAT, fall back to env token
  const session = await getSession(request.cookies.get('scriba.access_token')?.value);
  const ghAccount = session ? await dbHelpers.getGithubToken(session.user.id) : null;
  const token = ghAccount?.accessToken || process.env.GITHUB_TOKEN;

  try {
    // Fetch all in parallel
    const [repoInfo, treeData, languagesData, commitsData, contributorsData, readmeData] = await Promise.all([
      ghFetch(`/repos/${owner}/${repo}`, token, true),
      ghFetch(`/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`, token, true),
      ghFetch(`/repos/${owner}/${repo}/languages`, token),
      ghFetch(`/repos/${owner}/${repo}/commits?sha=${branch}&per_page=10`, token),
      ghFetch(`/repos/${owner}/${repo}/contributors?per_page=10`, token),
      ghFetch(`/repos/${owner}/${repo}/readme`, token),
    ]);

    // Build file tree structure
    const fileTree = buildFileTree(treeData?.tree || []);

    // Calculate file stats
    const allFiles = (treeData?.tree || []).filter((t: any) => t.type === 'blob');
    const totalFiles = allFiles.length;
    const extensions = countExtensions(allFiles);

    // Language breakdown (GitHub returns bytes per language)
    const totalBytes = Object.values(languagesData || {}).reduce((a: number, b: any) => a + (b as number), 0);
    const languages = Object.entries(languagesData || {}).map(([name, bytes]) => ({
      name,
      bytes: bytes as number,
      percentage: totalBytes > 0 ? Math.round(((bytes as number) / totalBytes) * 1000) / 10 : 0,
    })).sort((a, b) => b.bytes - a.bytes);

    // Format commits
    const commits = (commitsData || []).map((c: any) => ({
      sha: c.sha?.substring(0, 7),
      message: c.commit?.message?.split('\n')[0] || '',
      author: c.commit?.author?.name || c.author?.login || 'Unknown',
      avatar: c.author?.avatar_url || '',
      date: c.commit?.author?.date || '',
    }));

    // Format contributors
    const contributors = (contributorsData || []).map((c: any) => ({
      login: c.login,
      avatar: c.avatar_url,
      contributions: c.contributions,
    }));

    // Decode README
    let readmeContent = '';
    if (readmeData?.content) {
      try {
        readmeContent = Buffer.from(readmeData.content, 'base64').toString('utf-8');
      } catch { /* ignore */ }
    }

    return NextResponse.json({
      repo: repoInfo ? {
        name: repoInfo.name,
        fullName: repoInfo.full_name,
        description: repoInfo.description,
        defaultBranch: repoInfo.default_branch,
        stars: repoInfo.stargazers_count,
        forks: repoInfo.forks_count,
        watchers: repoInfo.watchers_count,
        openIssues: repoInfo.open_issues_count,
        size: repoInfo.size, // KB
        createdAt: repoInfo.created_at,
        updatedAt: repoInfo.updated_at,
        pushedAt: repoInfo.pushed_at,
        license: repoInfo.license?.spdx_id || null,
        topics: repoInfo.topics || [],
        visibility: repoInfo.visibility || (repoInfo.private ? 'private' : 'public'),
      } : null,
      fileTree,
      totalFiles,
      extensions,
      languages,
      commits,
      contributors,
      readme: readmeContent,
    });
  } catch (error: any) {
    if (error?.message === 'rate_limit') {
      return NextResponse.json(
        { error: 'GitHub API rate limit exceeded. Add a GITHUB_TOKEN to .env or wait a few minutes and try again.' },
        { status: 429 }
      );
    }
    if (error?.message === 'not_found') {
      return NextResponse.json({ error: 'Repository not found or not accessible.' }, { status: 404 });
    }
    console.error('GitHub API error:', error);
    return NextResponse.json({ error: 'Failed to fetch repository data' }, { status: 500 });
  }
}

interface GitTreeItem {
  path: string;
  type: 'blob' | 'tree';
  size?: number;
}

interface TreeNode {
  name: string;
  type: 'file' | 'folder';
  path: string;
  size?: string;
  children?: TreeNode[];
}

function buildFileTree(items: GitTreeItem[]): TreeNode[] {
  const root: TreeNode[] = [];
  const map = new Map<string, TreeNode>();

  // Sort so directories come first
  const sorted = [...items].sort((a, b) => {
    if (a.type !== b.type) return a.type === 'tree' ? -1 : 1;
    return a.path.localeCompare(b.path);
  });

  for (const item of sorted) {
    const parts = item.path.split('/');
    const name = parts[parts.length - 1];
    const node: TreeNode = {
      name,
      type: item.type === 'tree' ? 'folder' : 'file',
      path: item.path,
      size: item.size ? formatSize(item.size) : undefined,
      children: item.type === 'tree' ? [] : undefined,
    };

    map.set(item.path, node);

    if (parts.length === 1) {
      root.push(node);
    } else {
      const parentPath = parts.slice(0, -1).join('/');
      const parent = map.get(parentPath);
      if (parent?.children) {
        parent.children.push(node);
      }
    }
  }

  return root;
}

function formatSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

function countExtensions(files: GitTreeItem[]): { ext: string; count: number; percentage: number }[] {
  const counts: Record<string, number> = {};
  for (const f of files) {
    const ext = f.path.includes('.') ? '.' + f.path.split('.').pop()!.toLowerCase() : '(no ext)';
    counts[ext] = (counts[ext] || 0) + 1;
  }
  const total = files.length;
  return Object.entries(counts)
    .map(([ext, count]) => ({
      ext,
      count,
      percentage: total > 0 ? Math.round((count / total) * 1000) / 10 : 0,
    }))
    .sort((a, b) => b.count - a.count);
}
