import { isGitRepo, getStagedDiff } from "@/utils/git";
import { info, Spinner, color } from "@/utils/formatter";
import { ProviderRegistry } from "@/utils/provider-registry";
import { formatPrompt } from "@/prompts";
import { ConfigManager } from "@/config/manager";
import { mergeWithEnvironment } from "@/utils/cli-options";
import { AI_DEFAULTS } from "@/config/defaults";
import { 
  handleError, 
  handleWarning, 
  GitError, 
  ValidationError,
  ProviderError 
} from "@/utils/error-handler";
import {
  parseReviewResponse,
  formatReviewAsText,
  formatReviewAsGitHub,
  formatReviewAsJson
} from "./formatter";
import type { CommonOptions } from "@/utils/cli-options";

interface ReviewOptions extends CommonOptions {
  target?: 'staged' | 'branch' | 'commit';
}

// Error messages constants
const ERROR_MESSAGES = {
  NOT_GIT_REPO: "Not a git repository",
  NO_STAGED_CHANGES: "No staged changes to review",
  STAGE_HINT: "Stage your changes with: git add <files>",
  TARGET_NOT_IMPLEMENTED: (target: string) => 
    `Review target '${target}' not yet implemented. Currently only 'staged' is supported.`,
  DIFF_FAILED: (err: string) => `Failed to get diff: ${err}`,
  REVIEW_FAILED: (err: string) => `Failed to generate review: ${err}`,
} as const;

export async function reviewCommand(options: ReviewOptions): Promise<void> {
  // Merge with environment variables
  const mergedOptions = mergeWithEnvironment(options);
  const outputFormat = mergedOptions.output || 'text';
  
  try {
    if (!(await isGitRepo())) {
      throw new GitError(ERROR_MESSAGES.NOT_GIT_REPO);
    }

    // Get diff based on target
    let diff: string;
    const target = mergedOptions.target || 'staged';
    
    if (target === 'staged') {
      try {
        diff = await getStagedDiff();
      } catch (err) {
        throw new GitError(
          ERROR_MESSAGES.DIFF_FAILED(err instanceof Error ? err.message : String(err))
        );
      }
      
      if (!diff.trim()) {
        // Intentionally return early without throwing an error
        // This is a common scenario (no changes) that shouldn't be treated as failure
        handleWarning(ERROR_MESSAGES.NO_STAGED_CHANGES, outputFormat);
        if (outputFormat === 'text') {
          info(ERROR_MESSAGES.STAGE_HINT);
        }
        return; // Exit gracefully without error code
      }
    } else {
      // TODO: Implement branch and commit diff
      throw new ValidationError(ERROR_MESSAGES.TARGET_NOT_IMPLEMENTED(target));
    }

    const spinner = new Spinner("Analyzing code for review...");
    if (outputFormat === 'text') {
      spinner.start();
    }

    try {
      const defaultProvider = options.provider || (await ConfigManager.get("defaultProvider"));
      const provider = ProviderRegistry.getProvider(defaultProvider);
      
      const prompt = formatPrompt("review", { diff });
      
      const generateOptions = {
        temperature: mergedOptions.temperature ?? AI_DEFAULTS.temperature.review,
        maxTokens: mergedOptions.maxTokens ?? AI_DEFAULTS.maxTokens.review,
        model: mergedOptions.model,
      };

      let response: string;
      
      // Use structured output if provider supports it
      if (provider.supportsStructuredOutput?.()) {
        const { reviewSchema } = await import("@/utils/schemas");
        response = await provider.generateStructuredContent?.(
          prompt,
          reviewSchema,
          generateOptions,
        ) ?? await provider.generateContent(prompt, generateOptions);
      } else {
        // Fallback to regular generation for providers without structured output
        response = await provider.generateContent(prompt, generateOptions);
      }
      
      if (outputFormat === 'text') {
        spinner.stop();
      }

      // Parse and format the review response
      const reviewResult = parseReviewResponse(response);
      
      if (reviewResult) {
        // Format based on output format
        switch (outputFormat) {
          case 'github':
            console.log(formatReviewAsGitHub(reviewResult));
            break;
          case 'json':
            console.log(formatReviewAsJson(reviewResult));
            break;
          case 'text':
          default:
            console.log(formatReviewAsText(reviewResult));
            break;
        }
      } else {
        // Fallback to raw output if parsing fails
        console.error(color("Warning: Could not parse AI review response into expected format. Displaying raw response.", "yellow"));
        console.log(response);
      }
      
    } catch (err) {
      if (outputFormat === 'text') {
        spinner.stop();
      }
      throw new ProviderError(
        ERROR_MESSAGES.REVIEW_FAILED(err instanceof Error ? err.message : String(err))
      );
    }
  } catch (err) {
    handleError(err, outputFormat);
  }
}