/**
 * Commit Storage
 * Handles filesystem operations for commits
 */

import * as fs from 'fs';
import * as path from 'path';
import { Commit, CommitIndex, CommitMetadata, BangstackDirectory } from './types';

export class CommitStorage {
  private bangstackDir: BangstackDirectory;

  constructor(projectRoot: string) {
    const bangstackRoot = path.join(projectRoot, '.bangstack');

    this.bangstackDir = {
      root: bangstackRoot,
      config: path.join(bangstackRoot, 'config.yaml'),
      commits: path.join(bangstackRoot, 'commits'),
      commitIndex: path.join(bangstackRoot, 'commits', 'index.json'),
      state: path.join(bangstackRoot, 'state.json'),
      cache: path.join(bangstackRoot, 'cache'),
    };
  }

  /**
   * Initialize .bangstack directory structure
   */
  initialize(): void {
    // Create directories
    if (!fs.existsSync(this.bangstackDir.root)) {
      fs.mkdirSync(this.bangstackDir.root, { recursive: true });
    }

    if (!fs.existsSync(this.bangstackDir.commits)) {
      fs.mkdirSync(this.bangstackDir.commits, { recursive: true });
    }

    if (!fs.existsSync(this.bangstackDir.cache)) {
      fs.mkdirSync(this.bangstackDir.cache, { recursive: true });
    }

    // Create initial index if it doesn't exist
    if (!fs.existsSync(this.bangstackDir.commitIndex)) {
      const initialIndex: CommitIndex = {
        head: null,
        commits: [],
      };
      this.saveIndex(initialIndex);
    }
  }

  /**
   * Check if .bangstack directory exists
   */
  exists(): boolean {
    return fs.existsSync(this.bangstackDir.root);
  }

  /**
   * Save a commit to disk
   */
  saveCommit(commit: Commit): void {
    const commitPath = path.join(this.bangstackDir.commits, `${commit.hash}.json`);
    fs.writeFileSync(commitPath, JSON.stringify(commit, null, 2));
  }

  /**
   * Load a commit from disk
   */
  loadCommit(hash: string): Commit | null {
    const commitPath = path.join(this.bangstackDir.commits, `${hash}.json`);

    if (!fs.existsSync(commitPath)) {
      return null;
    }

    const content = fs.readFileSync(commitPath, 'utf-8');
    return JSON.parse(content);
  }

  /**
   * Load commit index
   */
  loadIndex(): CommitIndex {
    if (!fs.existsSync(this.bangstackDir.commitIndex)) {
      return { head: null, commits: [] };
    }

    const content = fs.readFileSync(this.bangstackDir.commitIndex, 'utf-8');
    return JSON.parse(content);
  }

  /**
   * Save commit index
   */
  saveIndex(index: CommitIndex): void {
    fs.writeFileSync(
      this.bangstackDir.commitIndex,
      JSON.stringify(index, null, 2)
    );
  }

  /**
   * Update index with new commit
   */
  updateIndex(commit: Commit): void {
    const index = this.loadIndex();

    // Add commit metadata to index
    const metadata: CommitMetadata = {
      hash: commit.hash,
      parent: commit.parent,
      timestamp: commit.timestamp,
      author: commit.author,
      message: commit.message,
    };

    index.commits.push(metadata);
    index.head = commit.hash;

    this.saveIndex(index);
  }

  /**
   * Get commit history (like git log)
   */
  getHistory(limit?: number): CommitMetadata[] {
    const index = this.loadIndex();

    if (limit) {
      return index.commits.slice(-limit).reverse();
    }

    return [...index.commits].reverse();
  }

  /**
   * Get current HEAD commit
   */
  getHead(): Commit | null {
    const index = this.loadIndex();

    if (!index.head) {
      return null;
    }

    return this.loadCommit(index.head);
  }

  /**
   * Get config file path
   */
  getConfigPath(): string {
    return this.bangstackDir.config;
  }

  /**
   * Check if config file exists
   */
  hasConfig(): boolean {
    return fs.existsSync(this.bangstackDir.config);
  }

  /**
   * Load config file
   */
  loadConfig(): string {
    if (!this.hasConfig()) {
      throw new Error('Config file not found: .bangstack/config.yaml');
    }

    return fs.readFileSync(this.bangstackDir.config, 'utf-8');
  }

  /**
   * Save config file
   */
  saveConfig(content: string): void {
    fs.writeFileSync(this.bangstackDir.config, content);
  }

  /**
   * Get list of all commit hashes
   */
  listCommits(): string[] {
    const index = this.loadIndex();
    return index.commits.map(c => c.hash);
  }

  /**
   * Check if commit exists
   */
  hasCommit(hash: string): boolean {
    const commitPath = path.join(this.bangstackDir.commits, `${hash}.json`);
    return fs.existsSync(commitPath);
  }

  /**
   * Delete a commit (use with caution!)
   */
  deleteCommit(hash: string): void {
    const commitPath = path.join(this.bangstackDir.commits, `${hash}.json`);

    if (fs.existsSync(commitPath)) {
      fs.unlinkSync(commitPath);
    }

    // Update index
    const index = this.loadIndex();
    index.commits = index.commits.filter(c => c.hash !== hash);

    if (index.head === hash) {
      // Reset HEAD to previous commit
      const remainingCommits = index.commits;
      index.head = remainingCommits.length > 0
        ? remainingCommits[remainingCommits.length - 1].hash
        : null;
    }

    this.saveIndex(index);
  }

  /**
   * Get directory paths
   */
  getDirectories(): BangstackDirectory {
    return this.bangstackDir;
  }
}
