/**
 * Schema Cache Manager
 * Maintains an offline cache of Directus collections and fields schema
 * Updates cache on every create/update/delete operation
 */

const fs = require('fs').promises;
const path = require('path');
const { 
  readCollections, 
  readFields,
  readRelations
} = require('@directus/sdk');

class SchemaCache {
  constructor(cacheDir = null) {
    this.cacheDir = cacheDir || path.join(process.cwd(), '.directus-cache');
    this.cacheFile = path.join(this.cacheDir, 'schema.json');
    this.lockFile = path.join(this.cacheDir, 'schema.lock');
    this.cache = null;
    this.isDirty = false;
  }

  /**
   * Initialize cache directory and files
   */
  async init() {
    // Skip if already initialized
    if (this.cache !== null) {
      return true;
    }
    
    try {
      // Create cache directory if it doesn't exist
      await fs.mkdir(this.cacheDir, { recursive: true });
      
      // Try to load existing cache
      await this.load();
      
      return true;
    } catch (error) {
      console.error('❌ Failed to initialize cache:', error.message);
      return false;
    }
  }

  /**
   * Load cache from disk
   */
  async load() {
    try {
      const data = await fs.readFile(this.cacheFile, 'utf-8');
      this.cache = JSON.parse(data);
      return this.cache;
    } catch (error) {
      // If file doesn't exist, initialize empty cache
      if (error.code === 'ENOENT') {
        this.cache = {
          version: '1.0.0',
          lastSync: null,
          collections: {},
          relations: [],
          metadata: {
            totalCollections: 0,
            totalFields: 0,
            totalRelations: 0
          }
        };
        await this.save();
      } else {
        throw error;
      }
    }
    return this.cache;
  }

  /**
   * Save cache to disk
   */
  async save() {
    try {
      // Add timestamp
      this.cache.lastModified = new Date().toISOString();
      
      // Update metadata
      this.updateMetadata();
      
      // Write with pretty formatting for readability
      const data = JSON.stringify(this.cache, null, 2);
      await fs.writeFile(this.cacheFile, data, 'utf-8');
      
      this.isDirty = false;
      return true;
    } catch (error) {
      console.error('❌ Failed to save cache:', error.message);
      return false;
    }
  }

  /**
   * Update cache metadata
   */
  updateMetadata() {
    if (!this.cache) return;
    
    const collections = Object.keys(this.cache.collections);
    let totalFields = 0;
    
    collections.forEach(collection => {
      if (this.cache.collections[collection].fields) {
        totalFields += Object.keys(this.cache.collections[collection].fields).length;
      }
    });
    
    this.cache.metadata = {
      totalCollections: collections.length,
      totalFields: totalFields,
      totalRelations: this.cache.relations ? this.cache.relations.length : 0
    };
  }

  /**
   * Sync cache with Directus instance
   */
  async sync(client) {
    try {
      console.log('🔄 Syncing schema cache with Directus...');
      
      // Fetch all collections
      const collections = await client.request(readCollections());
      
      // Reset cache
      this.cache = {
        version: '1.0.0',
        lastSync: new Date().toISOString(),
        collections: {},
        relations: [],
        metadata: {}
      };
      
      // Process each collection
      for (const collection of collections) {
        // Skip system collections unless specified
        if (collection.collection.startsWith('directus_') && !collection.meta?.system) {
          continue;
        }
        
        // Store collection info
        this.cache.collections[collection.collection] = {
          name: collection.collection,
          meta: collection.meta,
          schema: collection.schema,
          fields: {},
          created: new Date().toISOString()
        };
        
        // Fetch fields for this collection
        try {
          const fields = await client.request(readFields(collection.collection));
          
          // Store field info
          fields.forEach(field => {
            this.cache.collections[collection.collection].fields[field.field] = {
              field: field.field,
              type: field.type,
              meta: field.meta,
              schema: field.schema
            };
          });
        } catch (fieldError) {
          console.warn(`  ⚠️  Could not fetch fields for ${collection.collection}:`, fieldError.message);
        }
      }
      
      // Fetch all relations
      try {
        const relations = await client.request(readRelations());
        this.cache.relations = relations;
      } catch (relError) {
        console.warn('  ⚠️  Could not fetch relations:', relError.message);
      }
      
      // Save to disk
      await this.save();
      
      console.log(`✅ Synced ${Object.keys(this.cache.collections).length} collections`);
      console.log(`📊 Cache statistics:`, this.cache.metadata);
      
      return this.cache;
    } catch (error) {
      console.error('❌ Failed to sync cache:', error.message);
      throw error;
    }
  }

  /**
   * Check if a collection exists in cache
   */
  hasCollection(collectionName) {
    return this.cache && 
           this.cache.collections && 
           this.cache.collections[collectionName] !== undefined;
  }

  /**
   * Check if a field exists in cache
   */
  hasField(collectionName, fieldName) {
    return this.hasCollection(collectionName) &&
           this.cache.collections[collectionName].fields &&
           this.cache.collections[collectionName].fields[fieldName] !== undefined;
  }

  /**
   * Get collection info from cache
   */
  getCollection(collectionName) {
    if (!this.hasCollection(collectionName)) {
      return null;
    }
    return this.cache.collections[collectionName];
  }

  /**
   * Get field info from cache
   */
  getField(collectionName, fieldName) {
    if (!this.hasField(collectionName, fieldName)) {
      return null;
    }
    return this.cache.collections[collectionName].fields[fieldName];
  }

  /**
   * Add or update collection in cache
   */
  async addCollection(collectionName, collectionData = {}) {
    if (!this.cache) await this.load();
    
    this.cache.collections[collectionName] = {
      name: collectionName,
      meta: collectionData.meta || {},
      schema: collectionData.schema || {},
      fields: collectionData.fields || {},
      created: collectionData.created || new Date().toISOString(),
      modified: new Date().toISOString()
    };
    
    this.isDirty = true;
    await this.save();
    
    return this.cache.collections[collectionName];
  }

  /**
   * Add or update field in cache
   */
  async addField(collectionName, fieldName, fieldData = {}) {
    if (!this.cache) await this.load();
    
    // Ensure collection exists
    if (!this.hasCollection(collectionName)) {
      await this.addCollection(collectionName);
    }
    
    // Ensure fields object exists
    if (!this.cache.collections[collectionName].fields) {
      this.cache.collections[collectionName].fields = {};
    }
    
    // Add or update field
    this.cache.collections[collectionName].fields[fieldName] = {
      field: fieldName,
      type: fieldData.type,
      meta: fieldData.meta || {},
      schema: fieldData.schema || {},
      created: fieldData.created || new Date().toISOString(),
      modified: new Date().toISOString()
    };
    
    this.isDirty = true;
    await this.save();
    
    return this.cache.collections[collectionName].fields[fieldName];
  }

  /**
   * Remove collection from cache
   */
  async removeCollection(collectionName) {
    if (!this.cache) await this.load();
    
    if (this.hasCollection(collectionName)) {
      delete this.cache.collections[collectionName];
      this.isDirty = true;
      await this.save();
      return true;
    }
    
    return false;
  }

  /**
   * Remove field from cache
   */
  async removeField(collectionName, fieldName) {
    if (!this.cache) await this.load();
    
    if (this.hasField(collectionName, fieldName)) {
      delete this.cache.collections[collectionName].fields[fieldName];
      this.isDirty = true;
      await this.save();
      return true;
    }
    
    return false;
  }

  /**
   * Rename field in cache
   */
  async renameField(collectionName, oldFieldName, newFieldName) {
    if (!this.cache) await this.load();
    
    if (this.hasField(collectionName, oldFieldName)) {
      const fieldData = this.cache.collections[collectionName].fields[oldFieldName];
      fieldData.field = newFieldName;
      fieldData.modified = new Date().toISOString();
      fieldData.previousName = oldFieldName;
      
      // Add new field name
      this.cache.collections[collectionName].fields[newFieldName] = fieldData;
      
      // Remove old field name
      delete this.cache.collections[collectionName].fields[oldFieldName];
      
      this.isDirty = true;
      await this.save();
      
      return true;
    }
    
    return false;
  }

  /**
   * Add or update relation in cache
   */
  async addRelation(relationData) {
    if (!this.cache) await this.load();
    
    if (!this.cache.relations) {
      this.cache.relations = [];
    }
    
    // Check if relation already exists
    const existingIndex = this.cache.relations.findIndex(r => 
      r.collection === relationData.collection && 
      r.field === relationData.field
    );
    
    if (existingIndex >= 0) {
      // Update existing relation
      this.cache.relations[existingIndex] = {
        ...relationData,
        modified: new Date().toISOString()
      };
    } else {
      // Add new relation
      this.cache.relations.push({
        ...relationData,
        created: new Date().toISOString()
      });
    }
    
    this.isDirty = true;
    await this.save();
    
    return true;
  }

  /**
   * Get cache statistics
   */
  getStats() {
    if (!this.cache) {
      return {
        initialized: false
      };
    }
    
    const collections = Object.keys(this.cache.collections);
    let totalFields = 0;
    let systemCollections = 0;
    let customCollections = 0;
    
    collections.forEach(collection => {
      if (collection.startsWith('directus_')) {
        systemCollections++;
      } else {
        customCollections++;
      }
      
      if (this.cache.collections[collection].fields) {
        totalFields += Object.keys(this.cache.collections[collection].fields).length;
      }
    });
    
    return {
      initialized: true,
      lastSync: this.cache.lastSync,
      lastModified: this.cache.lastModified,
      totalCollections: collections.length,
      systemCollections,
      customCollections,
      totalFields,
      totalRelations: this.cache.relations ? this.cache.relations.length : 0,
      cacheFile: this.cacheFile
    };
  }

  /**
   * Clear the cache
   */
  async clear() {
    this.cache = {
      version: '1.0.0',
      lastSync: null,
      collections: {},
      relations: [],
      metadata: {}
    };
    
    await this.save();
    console.log('🗑️  Cache cleared');
    return true;
  }

  /**
   * Export cache for debugging
   */
  async export(outputFile = null) {
    if (!this.cache) await this.load();
    
    const exportFile = outputFile || path.join(this.cacheDir, `schema-export-${Date.now()}.json`);
    const data = JSON.stringify(this.cache, null, 2);
    
    await fs.writeFile(exportFile, data, 'utf-8');
    console.log(`📤 Cache exported to: ${exportFile}`);
    
    return exportFile;
  }
}

// Singleton instance
let cacheInstance = null;

/**
 * Get or create cache instance
 */
function getCache(cacheDir = null) {
  if (!cacheInstance) {
    cacheInstance = new SchemaCache(cacheDir);
  }
  return cacheInstance;
}

module.exports = {
  SchemaCache,
  getCache
};