/**
 * Clone Service
 *
 * Duplicates a company and all its associated data into a new company,
 * remapping all cross-document IDs so the cloned data is internally consistent.
 */

import { getModels } from '../models';

// ============================================
// TYPE HELPERS
// ============================================

interface CloneResult {
  company: any;
  stats: {
    modelsCloned: Record<string, number>;
    totalDocuments: number;
  };
}

// ============================================
// ID REMAPPER
// ============================================

class IdRemapper {
  private maps: Map<string, Map<string, string>> = new Map();

  register(modelName: string, oldId: string, newId: string): void {
    if (!this.maps.has(modelName)) {
      this.maps.set(modelName, new Map());
    }
    this.maps.get(modelName)!.set(oldId, newId);
  }

  remap(modelName: string, oldId: string | undefined | null): string | undefined {
    if (!oldId) return undefined;
    const map = this.maps.get(modelName);
    return map?.get(oldId) || oldId;
  }

  remapArray(modelName: string, oldIds: (string | undefined | null)[] | undefined): string[] {
    if (!oldIds || !Array.isArray(oldIds)) return [];
    return oldIds
      .map((id) => this.remap(modelName, id))
      .filter(Boolean) as string[];
  }
}

// ============================================
// CLONE HELPERS
// ============================================

/**
 * Generate a new unique ID for a cloned document.
 * Uses MongoDB-compatible ObjectId format (24 hex chars).
 */
function generateNewId(): string {
  const timestamp = Math.floor(Date.now() / 1000).toString(16).padStart(8, '0');
  const random = Array.from({ length: 16 }, () =>
    Math.floor(Math.random() * 16).toString(16)
  ).join('');
  return timestamp + random;
}

/**
 * Clone a single document: remove _id and id, set companyId to newCompanyId,
 * and return the plain object ready for insertion.
 */
function cloneDocument(doc: any, newCompanyId: string): any {
  const obj = doc.toObject ? doc.toObject() : { ...doc };
  delete obj._id;
  delete obj.id;
  obj.companyId = newCompanyId;
  obj.createdAt = new Date();
  obj.updatedAt = new Date();
  return obj;
}

// ============================================
// MODELS TO CLONE
// ============================================

/**
 * Definition of models to clone, in dependency order.
 * Tier 1 models have no cross-references to other model types.
 * Tier 2 models reference Tier 1 models.
 * Tier 3 models reference Tier 1 and Tier 2 models.
 * Hierarchical models (parent-child) need special handling.
 *
 * Format: [modelName, idRemapKey, fieldsToRemap]
 * - modelName: the key in getModels()
 * - idRemapKey: the key used in the IdRemapper for this model's IDs
 * - fieldsToRemap: object mapping field paths to {model, type}
 *   where type is 'id' (single string) or 'array' (string array)
 */

const CLONE_MODELS_TIER1: Array<{
  model: string;
  remapKey: string;
  remapFields?: Record<string, { model: string; type: 'id' | 'array' }>;
  singleton?: boolean;
}> = [
  // Foundation - singletons
  { model: 'BusinessProfile', remapKey: 'businessProfile', singleton: true },
  // Foundation - arrays
  { model: 'Founder', remapKey: 'founder', remapFields: {} },
  { model: 'Employee', remapKey: 'employee', remapFields: { reportsTo: { model: 'employee', type: 'id' } } },
  { model: 'ProductCategory', remapKey: 'productCategory', remapFields: { parentId: { model: 'productCategory', type: 'id' } } },
  { model: 'Product', remapKey: 'product', remapFields: { categoryId: { model: 'productCategory', type: 'id' }, icpIds: { model: 'icp', type: 'array' }, personaIds: { model: 'persona', type: 'array' } } },
  { model: 'Competitor', remapKey: 'competitor' },
  // Brand - singletons and arrays
  { model: 'Brand', remapKey: 'brand', singleton: true },
  { model: 'BrandAsset', remapKey: 'brandAsset' },
  { model: 'Stationery', remapKey: 'stationery' },
  { model: 'HRAsset', remapKey: 'hrAsset' },
  // Content - singletons and arrays
  { model: 'BlogContentOS', remapKey: 'blogContentOS', singleton: true },
  { model: 'NewsletterContentOS', remapKey: 'newsletterContentOS', singleton: true },
  { model: 'LandingPageContentOS', remapKey: 'landingPageContentOS', singleton: true },
  // Misc simple models
  { model: 'Newsletter', remapKey: 'newsletter' },
  { model: 'MembershipPlan', remapKey: 'membershipPlan' },
  { model: 'EmailTemplate', remapKey: 'emailTemplate' },
  { model: 'EmailTemplateCategory', remapKey: 'emailTemplateCategory' },
  { model: 'LegalDocument', remapKey: 'legalDocument' },
  { model: 'LoyaltyProgramme', remapKey: 'loyaltyProgramme' },
  { model: 'Influencer', remapKey: 'influencer' },
  { model: 'MediaMention', remapKey: 'mediaMention' },
  { model: 'ReputationReview', remapKey: 'reputationReview' },
  { model: 'Complaint', remapKey: 'complaint' },
  { model: 'Award', remapKey: 'award' },
  { model: 'ModuleData', remapKey: 'moduleData', singleton: false },
];

const CLONE_MODELS_TIER2: Array<{
  model: string;
  remapKey: string;
  remapFields?: Record<string, { model: string; type: 'id' | 'array' }>;
}> = [
  // ICP and Persona reference each other and products
  { model: 'ICP', remapKey: 'icp', remapFields: { personaIds: { model: 'persona', type: 'array' } } },
  { model: 'Persona', remapKey: 'persona', remapFields: { icpId: { model: 'icp', type: 'id' }, productIds: { model: 'product', type: 'array' } } },
  // Categories that reference other categories (parentId)
  { model: 'FAQCategory', remapKey: 'faqCategory', remapFields: { parentId: { model: 'faqCategory', type: 'id' } } },
  { model: 'BookCategory', remapKey: 'bookCategory', remapFields: { parentId: { model: 'bookCategory', type: 'id' } } },
  { model: 'SopCategory', remapKey: 'sopCategory', remapFields: { parentId: { model: 'sopCategory', type: 'id' } } },
  { model: 'CaseStudyCategory', remapKey: 'caseStudyCategory', remapFields: { parentId: { model: 'caseStudyCategory', type: 'id' } } },
  { model: 'CourseCategory', remapKey: 'courseCategory', remapFields: { parentId: { model: 'courseCategory', type: 'id' } } },
  { model: 'EventCategory', remapKey: 'eventCategory', remapFields: { parentId: { model: 'eventCategory', type: 'id' } } },
  { model: 'CollateralCategoryInfo', remapKey: 'collateralCategory' },
  { model: 'VideoCategoryInfo', remapKey: 'videoCategory' },
  { model: 'VideoPlaylist', remapKey: 'videoPlaylist' },
  // Website
  { model: 'WebsitePage', remapKey: 'websitePage', remapFields: { parentId: { model: 'websitePage', type: 'id' } } },
  // Blogs
  { model: 'Blog', remapKey: 'blog' },
  // SEO
  { model: 'SEOPage', remapKey: 'seoPage' },
  // Ad campaigns (parent of ads, audiences, budgets)
  { model: 'AdCampaign', remapKey: 'adCampaign' },
  // Referral
  { model: 'ReferralOffer', remapKey: 'referralOffer' },
  // Testimonials reference many entities
  { model: 'Testimonial', remapKey: 'testimonial', remapFields: {
    productIds: { model: 'product', type: 'array' },
    founderIds: { model: 'founder', type: 'array' },
    employeeIds: { model: 'employee', type: 'array' },
  }},
  // FAQ
  { model: 'FAQ', remapKey: 'faq', remapFields: {
    categoryId: { model: 'faqCategory', type: 'id' },
    productId: { model: 'product', type: 'id' },
    parentFaqId: { model: 'faq', type: 'id' },
    relatedFaqIds: { model: 'faq', type: 'array' },
  }},
  // Sales
  { model: 'SalesScript', remapKey: 'salesScript', remapFields: { productId: { model: 'product', type: 'id' } } },
  { model: 'SalesCollateral', remapKey: 'salesCollateral', remapFields: {
    productIds: { model: 'product', type: 'array' },
    icpIds: { model: 'icp', type: 'array' },
  }},
  { model: 'VideoContent', remapKey: 'videoContent', remapFields: { productIds: { model: 'product', type: 'array' } } },
  // Landing pages
  { model: 'LandingPage', remapKey: 'landingPage' },
  { model: 'LandingPageTemplate', remapKey: 'landingPageTemplate' },
  { model: 'LandingPageExport', remapKey: 'landingPageExport' },
  // Blog Content OS sub-documents
  { model: 'BlogStrategy', remapKey: 'blogStrategy' },
  { model: 'BlogContentTypeConfig', remapKey: 'blogContentType' },
  { model: 'BlogCalendar', remapKey: 'blogCalendar' },
  { model: 'BlogSEOConfig', remapKey: 'blogSEOConfig' },
  { model: 'BlogTitle', remapKey: 'blogTitle' },
  { model: 'BlogPost', remapKey: 'blogPost' },
  { model: 'BlogContentChunk', remapKey: 'blogContentChunk' },
  { model: 'BlogExport', remapKey: 'blogExport' },
  { model: 'BlogStructure', remapKey: 'blogStructure' },
  { model: 'BlogContentSection', remapKey: 'blogContentSection' },
  // Newsletter Content OS sub-documents
  { model: 'NewsletterStrategy', remapKey: 'newsletterStrategy' },
  { model: 'NewsletterCalendar', remapKey: 'newsletterCalendar' },
  { model: 'NewsletterTitle', remapKey: 'newsletterTitle' },
  { model: 'NewsletterPost', remapKey: 'newsletterPost' },
  { model: 'NewsletterContentChunk', remapKey: 'newsletterContentChunk' },
  { model: 'NewsletterExport', remapKey: 'newsletterExport' },
  // Social Media OS
  { model: 'SocialContentStrategy', remapKey: 'socialContentStrategy' },
  { model: 'SocialCalendarEntry', remapKey: 'socialCalendarEntry' },
  { model: 'SocialContentTemplate', remapKey: 'socialContentTemplate' },
  { model: 'SocialHashtagBank', remapKey: 'socialHashtagBank' },
  { model: 'SocialCreative', remapKey: 'socialCreative' },
  { model: 'SocialCampaign', remapKey: 'socialCampaign' },
  { model: 'SocialExport', remapKey: 'socialExport' },
  // SEO OS
  { model: 'SeoStrategy', remapKey: 'seoStrategy' },
  { model: 'SeoRecord', remapKey: 'seoRecord' },
  { model: 'SeoKeyword', remapKey: 'seoKeyword' },
  { model: 'SeoKeywordBank', remapKey: 'seoKeywordBank' },
  { model: 'SeoAudit', remapKey: 'seoAudit' },
  { model: 'SeoCalendar', remapKey: 'seoCalendar' },
  { model: 'SeoCalendarItem', remapKey: 'seoCalendarItem' },
];

const CLONE_MODELS_TIER3: Array<{
  model: string;
  remapKey: string;
  remapFields?: Record<string, { model: string; type: 'id' | 'array' }>;
}> = [
  // Ads (reference campaigns)
  { model: 'Ad', remapKey: 'ad', remapFields: { campaignId: { model: 'adCampaign', type: 'id' } } },
  { model: 'AdAudience', remapKey: 'adAudience', remapFields: { campaignId: { model: 'adCampaign', type: 'id' } } },
  { model: 'AdBudget', remapKey: 'adBudget', remapFields: { campaignId: { model: 'adCampaign', type: 'id' }, audienceId: { model: 'adAudience', type: 'id' } } },
  { model: 'AdCreativeAsset', remapKey: 'adCreativeAsset', remapFields: { campaignId: { model: 'adCampaign', type: 'id' } } },
  { model: 'AdABTest', remapKey: 'adABTest', remapFields: { campaignId: { model: 'adCampaign', type: 'id' } } },
  { model: 'AdAIRecommendation', remapKey: 'adRecommendation', remapFields: { campaignId: { model: 'adCampaign', type: 'id' } } },
  // Influencer campaigns
  { model: 'InfluencerCampaign', remapKey: 'influencerCampaign' },
  // Books with hierarchical structure
  { model: 'Book', remapKey: 'book', remapFields: { categoryId: { model: 'bookCategory', type: 'id' } } },
  { model: 'BookChapter', remapKey: 'bookChapter', remapFields: { bookId: { model: 'book', type: 'id' } } },
  { model: 'BookSection', remapKey: 'bookSection', remapFields: { bookId: { model: 'book', type: 'id' }, chapterId: { model: 'bookChapter', type: 'id' } } },
  { model: 'BookContentBlock', remapKey: 'bookContentBlock', remapFields: { bookId: { model: 'book', type: 'id' }, chapterId: { model: 'bookChapter', type: 'id' }, sectionId: { model: 'bookSection', type: 'id' } } },
  // Courses with hierarchical structure
  { model: 'Course', remapKey: 'course', remapFields: { categoryId: { model: 'courseCategory', type: 'id' } } },
  { model: 'CourseChapter', remapKey: 'courseChapter', remapFields: { courseId: { model: 'course', type: 'id' } } },
  { model: 'CourseLesson', remapKey: 'courseLesson', remapFields: { courseId: { model: 'course', type: 'id' }, chapterId: { model: 'courseChapter', type: 'id' } } },
  // Events with hierarchical structure
  { model: 'Event', remapKey: 'event', remapFields: { categoryId: { model: 'eventCategory', type: 'id' } } },
  { model: 'EventSession', remapKey: 'eventSession', remapFields: { eventId: { model: 'event', type: 'id' } } },
  { model: 'EventResource', remapKey: 'eventResource', remapFields: { eventId: { model: 'event', type: 'id' }, sessionId: { model: 'eventSession', type: 'id' } } },
  // SOPs
  { model: 'SOP', remapKey: 'sop', remapFields: { categoryId: { model: 'sopCategory', type: 'id' } } },
  // Case Studies
  { model: 'CaseStudy', remapKey: 'caseStudy', remapFields: { categoryId: { model: 'caseStudyCategory', type: 'id' } } },
  // AiContext
  { model: 'AiContext', remapKey: 'aiContext', remapFields: { entityId: { model: 'icp', type: 'id' } } },
];

const ALL_CLONE_MODELS = [...CLONE_MODELS_TIER1, ...CLONE_MODELS_TIER2, ...CLONE_MODELS_TIER3];

// ============================================
// MAIN CLONE FUNCTION
// ============================================

/**
 * Clones a company and all its associated data.
 *
 * Process:
 * 1. Create a new Company document with copied fields + "(Copy)" suffix
 * 2. For each model, find all documents with source companyId
 * 3. Clone each document with new companyId and remapped cross-references
 * 4. Return the new company and clone statistics
 */
export async function cloneCompany(
  sourceCompanyId: string,
  newCompanyName: string,
  userId: string,
  options: {
    includeContent?: boolean;
    includeAIContext?: boolean;
  } = {}
): Promise<CloneResult> {
  const { includeContent = true, includeAIContext = false } = options;
  const models = getModels();
  const remapper = new IdRemapper();
  const stats: Record<string, number> = {};
  let totalDocuments = 0;

  // 1. Clone the Company document
  const sourceCompany = await models.Company.findById(sourceCompanyId);
  if (!sourceCompany) {
    throw new Error('Source company not found');
  }

  const newCompanyDoc = cloneDocument(sourceCompany, '');
  newCompanyDoc.name = newCompanyName;
  newCompanyDoc.userIds = [userId];
  newCompanyDoc.isActive = true;

  const newCompany = await models.Company.create(newCompanyDoc);
  const newCompanyId = newCompany.id || newCompany._id?.toString();

  // Register the company ID remapping
  remapper.register('company', sourceCompanyId, newCompanyId);
  stats['Company'] = 1;
  totalDocuments += 1;

  // 2. Clone all related data in tier order
  for (const modelDef of ALL_CLONE_MODELS) {
    // Skip AiContext if not requested
    if (modelDef.model === 'AiContext' && !includeAIContext) continue;

    const Model = models[modelDef.model];
    if (!Model) continue;

    try {
      // Find all documents for the source company
      let sourceDocs: any[];
      try {
        sourceDocs = await Model.find({ companyId: sourceCompanyId });
      } catch {
        // Some models may not exist in the schema yet
        continue;
      }

      if (!sourceDocs || sourceDocs.length === 0) continue;

      // For singleton models, only clone the first document
      const isSingleton = 'singleton' in modelDef && modelDef.singleton;
      const docsToClone = isSingleton ? sourceDocs.slice(0, 1) : sourceDocs;

      const clonedDocs: any[] = [];

      for (const doc of docsToClone) {
        const oldId = doc.id || doc._id?.toString();
        const clonedDoc = cloneDocument(doc, newCompanyId);

        // Remap cross-reference fields
        if (modelDef.remapFields) {
          for (const [field, remapDef] of Object.entries(modelDef.remapFields)) {
            if (remapDef.type === 'id') {
              const oldValue = clonedDoc[field];
              if (oldValue) {
                clonedDoc[field] = remapper.remap(remapDef.model, oldValue) || null;
              }
            } else if (remapDef.type === 'array') {
              const oldArray = clonedDoc[field];
              if (Array.isArray(oldArray)) {
                clonedDoc[field] = remapper.remapArray(remapDef.model, oldArray);
              }
            }
          }
        }

        // Handle nested objects with ID references (e.g., linkedData in SalesCollateral)
        if (clonedDoc.linkedData && typeof clonedDoc.linkedData === 'object') {
          clonedDoc.linkedData = remapNestedIds(clonedDoc.linkedData, remapper);
        }

        // Handle influencer campaign assignedInfluencers
        if (clonedDoc.assignedInfluencers && Array.isArray(clonedDoc.assignedInfluencers)) {
          clonedDoc.assignedInfluencers = clonedDoc.assignedInfluencers.map((inf: any) => ({
            ...inf,
            influencerId: inf.influencerId ? remapper.remap('influencer', inf.influencerId) : inf.influencerId,
          }));
        }

        // Handle loyalty programme defaultTierId
        if (clonedDoc.defaultTierId) {
          clonedDoc.defaultTierId = remapper.remap('loyaltyProgramme', clonedDoc.defaultTierId);
        }

        // Handle referral product IDs
        if (clonedDoc.productReferrals && Array.isArray(clonedDoc.productReferrals)) {
          clonedDoc.productReferrals = clonedDoc.productReferrals.map((ref: any) => ({
            ...ref,
            productId: ref.productId ? remapper.remap('product', ref.productId) : ref.productId,
          }));
        }

        // Handle loyalty earn/redeem rules with product references
        if (clonedDoc.earnRules && Array.isArray(clonedDoc.earnRules)) {
          clonedDoc.earnRules = clonedDoc.earnRules.map((rule: any) => ({
            ...rule,
            productId: rule.productId ? remapper.remap('product', rule.productId) : rule.productId,
          }));
        }
        if (clonedDoc.redeemRules && Array.isArray(clonedDoc.redeemRules)) {
          clonedDoc.redeemRules = clonedDoc.redeemRules.map((rule: any) => ({
            ...rule,
            productId: rule.productId ? remapper.remap('product', rule.productId) : rule.productId,
          }));
        }

        // Generate a new _id for the cloned document
        clonedDoc._id = undefined;
        delete clonedDoc.id;

        // Create the document
        try {
          const created = await Model.create(clonedDoc);
          const newId = created.id || created._id?.toString();

          // Register the ID mapping
          if (oldId && newId) {
            remapper.register(modelDef.remapKey, oldId, newId);
          }

          clonedDocs.push(created);
        } catch (createError: any) {
          console.error(`[CloneService] Error creating ${modelDef.model}:`, createError.message);
          // Continue with next document instead of failing the entire clone
        }
      }

      stats[modelDef.model] = clonedDocs.length;
      totalDocuments += clonedDocs.length;
    } catch (error: any) {
      console.error(`[CloneService] Error cloning ${modelDef.model}:`, error.message);
      // Continue with next model instead of failing the entire clone
    }
  }

  // 3. Add the new company to the user's company list
  const User = models.User;
  const user = await User.findById(userId);
  if (user) {
    if (!user.companyIds.includes(newCompanyId)) {
      user.companyIds.push(newCompanyId);
      await user.save();
    }
  }

  return {
    company: newCompany,
    stats: {
      modelsCloned: stats,
      totalDocuments,
    },
  };
}

/**
 * Remap ID references in nested objects like linkedData.
 */
function remapNestedIds(obj: any, remapper: IdRemapper): any {
  if (!obj || typeof obj !== 'object') return obj;

  if (Array.isArray(obj)) {
    return obj.map(item => remapNestedIds(item, remapper));
  }

  const result: any = {};
  for (const [key, value] of Object.entries(obj)) {
    // Known ID reference fields in nested objects
    if (key.endsWith('Ids') || key.endsWith('Id')) {
      if (Array.isArray(value)) {
        // Try to remap using common model names
        result[key] = value.map((id: string) => {
          for (const modelKey of ['persona', 'salesScript', 'faq', 'testimonial', 'blog', 'product', 'icp', 'founder', 'employee', 'sop', 'course', 'caseStudy']) {
            const remapped = remapper.remap(modelKey, id);
            if (remapped !== id) return remapped;
          }
          return id;
        });
      } else if (typeof value === 'string') {
        // Try common models
        for (const modelKey of ['persona', 'salesScript', 'faq', 'testimonial', 'blog', 'product', 'icp', 'founder', 'employee', 'sop', 'course', 'caseStudy']) {
          const remapped = remapper.remap(modelKey, value);
          if (remapped !== value) {
            result[key] = remapped;
            break;
          }
        }
        if (!result[key]) result[key] = value;
      } else {
        result[key] = value;
      }
    } else if (typeof value === 'object' && value !== null) {
      result[key] = remapNestedIds(value, remapper);
    } else {
      result[key] = value;
    }
  }
  return result;
}