/**
 * Schema Helper Functions
 * Reusable functions for creating Directus collections and fields
 * Now with integrated schema caching for offline tracking
 */

const { 
  readCollections, 
  createCollection: sdkCreateCollection,
  readFields,
  createField: sdkCreateField,
  createRelation: sdkCreateRelation
} = require('@directus/sdk');

const { getCache } = require('./schema-cache');

/**
 * Create a collection with error handling
 * @param {Object} client - Directus client
 * @param {Object} collectionConfig - Collection configuration
 * @returns {Promise<Object>} Created collection or existing collection
 */
async function createCollection(client, collectionConfig) {
  const cache = getCache();
  await cache.init();
  
  try {
    // First check cache
    if (cache.hasCollection(collectionConfig.collection)) {
      console.log(`ℹ️  Collection '${collectionConfig.collection}' already exists (cached)`);
      return cache.getCollection(collectionConfig.collection);
    }
    
    // If not in cache, check Directus
    const existingCollections = await client.request(readCollections());
    const exists = existingCollections.find(c => c.collection === collectionConfig.collection);
    
    if (exists) {
      console.log(`ℹ️  Collection '${collectionConfig.collection}' already exists`);
      // Update cache with existing collection
      await cache.addCollection(collectionConfig.collection, exists);
      return exists;
    }

    // Create collection
    const collection = await client.request(
      sdkCreateCollection(collectionConfig)
    );
    
    console.log(`✅ Created collection: ${collectionConfig.collection}`);
    
    // Update cache with new collection
    await cache.addCollection(collectionConfig.collection, collection);
    
    return collection;
  } catch (error) {
    console.error(`❌ Failed to create collection ${collectionConfig.collection}:`, error.message);
    throw error;
  }
}

/**
 * Create a field with error handling
 * @param {Object} client - Directus client
 * @param {string} collection - Collection name
 * @param {Object} fieldConfig - Field configuration
 * @returns {Promise<Object>} Created field or existing field
 */
async function createField(client, collection, fieldConfig) {
  const cache = getCache();
  await cache.init();
  
  try {
    // First check cache
    if (cache.hasField(collection, fieldConfig.field)) {
      console.log(`  ℹ️  Field '${fieldConfig.field}' already exists in '${collection}' (cached)`);
      return { ...cache.getField(collection, fieldConfig.field), alreadyExists: true };
    }
    
    // If not in cache, check Directus
    const existingFields = await client.request(readFields(collection));
    const exists = existingFields.find(f => f.field === fieldConfig.field);
    
    if (exists) {
      console.log(`  ℹ️  Field '${fieldConfig.field}' already exists in '${collection}'`);
      // Update cache with existing field
      await cache.addField(collection, fieldConfig.field, exists);
      return { ...exists, alreadyExists: true };
    }

    // Create field
    const field = await client.request(
      sdkCreateField(collection, fieldConfig)
    );
    
    console.log(`  ✅ Created field: ${fieldConfig.field}`);
    
    // Update cache with new field
    await cache.addField(collection, fieldConfig.field, field);
    
    return { ...field, created: true };
  } catch (error) {
    // Provide more detailed error information
    const errorMessage = error.response?.data?.errors?.[0]?.message || 
                        error.message || 
                        'Unknown error';
    console.error(`  ❌ Failed to create field ${fieldConfig.field} in ${collection}: ${errorMessage}`);
    
    // Return error info instead of throwing
    return { 
      field: fieldConfig.field, 
      error: true, 
      errorMessage 
    };
  }
}

/**
 * Create multiple fields for a collection
 * @param {Object} client - Directus client
 * @param {string} collection - Collection name
 * @param {Array} fields - Array of field configurations
 * @returns {Promise<Array>} Created fields
 */
async function createFields(client, collection, fields) {
  const cache = getCache();
  await cache.init();
  
  const results = {
    created: [],
    existing: [],
    errors: []
  };
  
  // Process all fields without saving cache after each one
  for (const fieldConfig of fields) {
    try {
      // First check cache
      if (cache.hasField(collection, fieldConfig.field)) {
        results.existing.push(fieldConfig.field);
        continue;
      }
      
      // If not in cache, check Directus
      const existingFields = await client.request(readFields(collection));
      const exists = existingFields.find(f => f.field === fieldConfig.field);
      
      if (exists) {
        // Add to cache without saving
        cache.cache.collections[collection] = cache.cache.collections[collection] || { fields: {} };
        cache.cache.collections[collection].fields[fieldConfig.field] = exists;
        results.existing.push(fieldConfig.field);
      } else {
        // Create field
        try {
          const field = await client.request(
            sdkCreateField(collection, fieldConfig)
          );
          
          console.log(`  ✅ Created field: ${fieldConfig.field}`);
          
          // Add to cache without saving
          cache.cache.collections[collection] = cache.cache.collections[collection] || { fields: {} };
          cache.cache.collections[collection].fields[fieldConfig.field] = field;
          results.created.push(fieldConfig.field);
        } catch (createError) {
          const errorMessage = createError.response?.data?.errors?.[0]?.message || 
                              createError.message || 
                              'Unknown error';
          console.error(`  ❌ Failed to create field ${fieldConfig.field}: ${errorMessage}`);
          results.errors.push({
            field: fieldConfig.field,
            message: errorMessage
          });
        }
      }
    } catch (error) {
      results.errors.push({
        field: fieldConfig.field,
        message: error.message
      });
    }
  }
  
  // Save cache once after all fields processed
  if (results.created.length > 0 || results.existing.length > 0) {
    await cache.save();
  }
  
  // Summary report
  if (results.created.length > 0) {
    console.log(`  ✅ Created ${results.created.length} new fields`);
  }
  if (results.existing.length > 0) {
    console.log(`  ℹ️  ${results.existing.length} fields already exist`);
  }
  if (results.errors.length > 0) {
    console.log(`  ⚠️  ${results.errors.length} fields had errors:`);
    results.errors.forEach(err => {
      console.log(`     - ${err.field}: ${err.message}`);
    });
  }
  
  return results;
}

/**
 * Create a relationship between collections
 * @param {Object} client - Directus client
 * @param {Object} relationConfig - Relationship configuration
 * @returns {Promise<Object>} Created relationship
 */
async function createRelationship(client, relationConfig) {
  const cache = getCache();
  await cache.init();
  
  try {
    const relation = await client.request(
      sdkCreateRelation(relationConfig)
    );
    
    console.log(`✅ Created relationship: ${relationConfig.collection} -> ${relationConfig.related_collection}`);
    
    // Update cache with new relation
    await cache.addRelation(relationConfig);
    
    return relation;
  } catch (error) {
    if (error.message.includes('already exists')) {
      console.log(`ℹ️  Relationship already exists: ${relationConfig.collection} -> ${relationConfig.related_collection}`);
      // Still update cache even if it exists
      await cache.addRelation(relationConfig);
      return null;
    }
    console.error(`❌ Failed to create relationship:`, error.message);
    throw error;
  }
}

/**
 * Standard field configurations
 */
const FIELD_TYPES = {
  uuid: (field, options = {}) => ({
    field,
    type: 'uuid',
    schema: {
      is_primary_key: options.primary_key || false,
      has_auto_increment: false,
      is_nullable: options.nullable || false,
      default_value: options.default || null
    },
    meta: {
      interface: 'input',
      special: options.special || null,
      options: options.options || null,
      display: options.display || null,
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'full',
      ...options.meta
    }
  }),

  string: (field, options = {}) => ({
    field,
    type: 'string',
    schema: {
      is_nullable: options.nullable !== false,
      max_length: options.max_length || 255,
      default_value: options.default || null
    },
    meta: {
      interface: options.interface || 'input',
      options: options.options || null,
      display: options.display || null,
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'full',
      required: options.required || false,
      ...options.meta
    }
  }),

  text: (field, options = {}) => ({
    field,
    type: 'text',
    schema: {
      is_nullable: options.nullable !== false,
      default_value: options.default || null
    },
    meta: {
      interface: options.interface || 'input-multiline',
      options: options.options || null,
      display: options.display || null,
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'full',
      ...options.meta
    }
  }),

  integer: (field, options = {}) => ({
    field,
    type: 'integer',
    schema: {
      is_nullable: options.nullable !== false,
      default_value: options.default || null
    },
    meta: {
      interface: options.interface || 'input',
      options: options.options || null,
      display: options.display || null,
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'half',
      ...options.meta
    }
  }),

  json: (field, options = {}) => ({
    field,
    type: 'json',
    schema: {
      is_nullable: options.nullable !== false,
      default_value: options.default || null
    },
    meta: {
      interface: options.interface || 'input-code',
      options: {
        language: 'json',
        ...options.options
      },
      display: options.display || 'raw',
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'full',
      ...options.meta
    }
  }),

  datetime: (field, options = {}) => ({
    field,
    type: 'timestamp',
    schema: {
      is_nullable: options.nullable !== false,
      default_value: options.default || null
    },
    meta: {
      interface: options.interface || 'datetime',
      special: options.special || null,
      options: options.options || null,
      display: options.display || 'datetime',
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'half',
      ...options.meta
    }
  }),

  boolean: (field, options = {}) => ({
    field,
    type: 'boolean',
    schema: {
      is_nullable: options.nullable || false,
      default_value: options.default !== undefined ? options.default : false
    },
    meta: {
      interface: options.interface || 'boolean',
      special: options.special || null,
      options: options.options || null,
      display: options.display || 'boolean',
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'half',
      ...options.meta
    }
  }),

  decimal: (field, options = {}) => ({
    field,
    type: 'decimal',
    schema: {
      is_nullable: options.nullable !== false,
      numeric_precision: options.precision || 10,
      numeric_scale: options.scale || 2,
      default_value: options.default || null
    },
    meta: {
      interface: options.interface || 'input',
      options: options.options || null,
      display: options.display || null,
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'half',
      ...options.meta
    }
  }),

  date: (field, options = {}) => ({
    field,
    type: 'date',
    schema: {
      is_nullable: options.nullable !== false,
      default_value: options.default || null
    },
    meta: {
      interface: options.interface || 'datetime',
      special: options.special || null,
      options: options.options || null,
      display: options.display || 'datetime',
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'half',
      ...options.meta
    }
  }),

  dropdown: (field, choices, options = {}) => ({
    field,
    type: 'string',
    schema: {
      is_nullable: options.nullable !== false,
      default_value: options.default || null,
      max_length: 255
    },
    meta: {
      interface: 'select-dropdown',
      options: {
        choices: choices.map(choice => 
          typeof choice === 'string' 
            ? { text: choice, value: choice }
            : choice
        )
      },
      display: options.display || null,
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'half',
      required: options.required || false,
      ...options.meta
    }
  }),

  m2o: (field, relatedCollection, options = {}) => ({
    field,
    type: 'uuid',
    schema: {
      is_nullable: options.nullable !== false,
      foreign_key_table: relatedCollection,
      foreign_key_column: 'id'
    },
    meta: {
      interface: 'select-dropdown-m2o',
      special: ['m2o'],
      options: {
        template: options.template || '{{id}}'
      },
      display: options.display || null,
      readonly: options.readonly || false,
      hidden: options.hidden || false,
      width: options.width || 'full',
      ...options.meta
    }
  })
};

/**
 * Create system fields (created_at, updated_at, etc.)
 * @returns {Array} Array of system field configurations
 */
function getSystemFields() {
  return [
    FIELD_TYPES.datetime('created_at', { 
      special: ['date-created'],
      readonly: true,
      hidden: false
    }),
    FIELD_TYPES.datetime('updated_at', { 
      special: ['date-updated'],
      readonly: true,
      hidden: false
    })
  ];
}

module.exports = {
  createCollection,
  createField,
  createFields,
  createRelationship,
  FIELD_TYPES,
  getSystemFields
};