/**
 * Page Generator
 *
 * Generates missing pages from template definitions
 *
 * @module core/scripts/build/registry/post-build/page-generator
 */

import { existsSync } from 'fs'
import { readFile, writeFile, mkdir } from 'fs/promises'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'

import { log, verbose } from '../../../utils/index.mjs'
import { cleanupOrphanedTemplates } from './route-cleanup.mjs'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
// Default rootDir for backwards compatibility (will be overridden by config)
let rootDir = join(__dirname, '../../../../../..')

/**
 * Convert string to PascalCase
 */
function toPascalCase(str) {
  return str
    .replace(/[^a-zA-Z0-9]/g, ' ')
    .split(' ')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
    .join('')
}

/**
 * Generate content for a regular page (page.tsx, error.tsx, etc.)
 */
function generateRegularPageContent(appPath, templatePath) {
  // Remove .tsx/.ts extension from import path (TypeScript doesn't allow file extensions in imports)
  const templatePathWithoutExtension = templatePath.replace(/\.(tsx|ts)$/, '')

  return `/**
 * Template page - directly imports from theme
 * Template: ${appPath}
 * Generated by: scripts/build-registry.mjs
 */
import TemplateComponent from '${templatePathWithoutExtension}'

// Direct export of the theme template (no fallback)
export default TemplateComponent
`
}

/**
 * Generate content for a layout page
 */
async function generateLayoutPageContent(appPath, componentName, templatePath) {
  if (templatePath) {
    // Remove .tsx/.ts extension from import path (TypeScript doesn't allow file extensions in imports)
    const templatePathWithoutExtension = templatePath.replace(/\.(tsx|ts)$/, '')

    // Convert import path alias to absolute file system path
    // @/contents/themes/... → /absolute/path/contents/themes/...
    // Try both .tsx and .ts extensions
    const baseTemplatePath = templatePath
      .replace('@/', rootDir + '/')
      .replace(/\.(tsx|ts)$/, '')
    const absoluteTemplatePath = existsSync(baseTemplatePath + '.tsx')
      ? baseTemplatePath + '.tsx'
      : baseTemplatePath + '.ts'

    // Check if template file has a default export (component) or just metadata
    let hasDefaultExport = false
    let hasMetadata = false

    try {
      const templateContent = await readFile(absoluteTemplatePath, 'utf8')
      hasDefaultExport = /export\s+default/.test(templateContent)
      hasMetadata = /export\s+const\s+metadata\s*=/.test(templateContent)
    } catch (error) {
      // If we can't read the file, assume it has a default export
      hasDefaultExport = true
    }

    if (hasDefaultExport) {
      // Template has a component - import and re-export it
      return `/**
 * Layout template - directly imports from theme
 * Template: ${appPath}
 * Generated by: scripts/build-registry.mjs
 */
import TemplateComponent from '${templatePathWithoutExtension}'

// Direct export of the theme template (no fallback)
export default TemplateComponent
`
    } else if (hasMetadata) {
      // Metadata-only template - re-export metadata and provide pass-through component
      return `/**
 * Layout template - metadata-only (PROTECTED_RENDER)
 * Template: ${appPath}
 * Generated by: scripts/build-registry.mjs
 */
import type { ReactNode } from 'react'

// Re-export metadata from theme template
export { metadata } from '${templatePathWithoutExtension}'

// Pass-through component (actual rendering blocked by PROTECTED_RENDER)
interface ${componentName}Props {
  children: ReactNode
}

export default function ${componentName}({ children }: ${componentName}Props) {
  return <>{children}</>
}
`
    } else {
      // Empty template file - provide basic wrapper
      return `/**
 * Layout template - empty template file
 * Template: ${appPath}
 * Generated by: scripts/build-registry.mjs
 */
interface ${componentName}Props {
  children: React.ReactNode
}

export default function ${componentName}({ children }: ${componentName}Props) {
  return <>{children}</>
}
`
    }
  } else {
    // Basic layout wrapper for parent directory structure (when no template exists)
    return `/**
 * Basic layout wrapper - maintains Next.js structure
 * Generated by: scripts/build-registry.mjs
 */
interface ${componentName}Props {
  children: React.ReactNode
}

export default function ${componentName}({ children }: ${componentName}Props) {
  return <>{children}</>
}
`
  }
}

/**
 * Add template resolver to existing layout content
 * Respects React directives and import order
 */
function addTemplateResolverToLayout(content, layoutPath) {
  let modifiedContent = content

  // Skip if already has template resolver
  if (content.includes("from '@/core/lib/template-resolver'") || content.includes("from '@/core/lib/registries/template-registry.client'")) {
    return content
  }

  // Find React directives ('use client', 'use server') at the start
  const directiveRegex = /^(\s*['"`]use (client|server)['"`]\s*\n?)/
  const directiveMatch = content.match(directiveRegex)
  const isClientComponent = directiveMatch && directiveMatch[2] === 'client'

  // Use client-safe resolver for client components
  const importStatement = isClientComponent
    ? `import { getTemplateOrDefaultClient } from '@/core/lib/registries/template-registry.client'\n`
    : `import { getTemplateOrDefault } from '@/core/lib/template-resolver'\n`

  const resolverFunction = isClientComponent ? 'getTemplateOrDefaultClient' : 'getTemplateOrDefault'

  if (directiveMatch) {
    // Insert import after directive
    const directive = directiveMatch[0]
    const afterDirective = content.substring(directive.length)
    modifiedContent = directive + `\n${importStatement}` + afterDirective
  } else {
    // Insert import at the beginning
    modifiedContent = importStatement + content
  }

  // Handle different export patterns
  // Pattern 1: export default function FunctionName(...) { ... }
  const inlineFunctionMatch = modifiedContent.match(/export\s+default\s+function\s+([^\s(]+)/)
  if (inlineFunctionMatch) {
    const componentName = inlineFunctionMatch[1]
    // Convert to: function FunctionName(...) { ... } then add wrapped export at end
    modifiedContent = modifiedContent.replace(/export\s+default\s+function/, 'function')
    modifiedContent += `\nexport default ${resolverFunction}('${layoutPath}', ${componentName})\n`
    return modifiedContent
  }

  // Pattern 2: export default ComponentName (component already declared)
  const exportMatch = modifiedContent.match(/export\s+default\s+([^\s;({]+)/)
  if (exportMatch) {
    const componentName = exportMatch[1]
    const newExport = `export default ${resolverFunction}('${layoutPath}', ${componentName})`
    modifiedContent = modifiedContent.replace(/export\s+default\s+[^\s;({]+/, newExport)
  }

  return modifiedContent
}

/**
 * Duplicate a layout file to (templates) directory with template resolver integration
 */
async function duplicateLayoutWithTemplateResolver(sourcePath, targetPath, layoutPath) {
  // Create target directory if it doesn't exist
  const targetDir = dirname(targetPath)
  await mkdir(targetDir, { recursive: true })

  try {
    // Read the original layout file
    const originalContent = await readFile(sourcePath, 'utf8')

    // Check if it already uses template resolver
    if (originalContent.includes('getTemplateOrDefault')) {
      // Already has template resolver, just copy
      await writeFile(targetPath, originalContent, 'utf8')
      return
    }

    // Parse the content to add template resolver
    const modifiedContent = addTemplateResolverToLayout(originalContent, layoutPath)
    await writeFile(targetPath, modifiedContent, 'utf8')

  } catch (error) {
    // If parsing fails, generate a basic layout wrapper
    const componentName = `DuplicatedLayout${toPascalCase(layoutPath.replace(/[^\w]/g, '_'))}`
    const basicLayoutContent = await generateLayoutPageContent(layoutPath, componentName, null)
    await writeFile(targetPath, basicLayoutContent, 'utf8')
    verbose(`Generated basic layout wrapper for ${layoutPath}`)
  }
}

/**
 * Generate required parent layouts for a template page
 * Duplicates layouts from app/ to app/(templates)/ to preserve Next.js hierarchy
 */
async function generateRequiredLayouts(appPath, templatesDir) {
  let layoutsGenerated = 0

  // Extract the directory path from app path (remove filename)
  // Example: app/dashboard/(main)/plugins/page.tsx -> dashboard/(main)/plugins
  const pathParts = appPath.replace('app/', '').replace(/\/[^\/]+$/, '').split('/')

  // Generate all parent directories and check for layouts
  let currentPath = ''
  for (const part of pathParts) {
    if (!part) continue

    currentPath = currentPath ? `${currentPath}/${part}` : part
    const layoutPath = `app/${currentPath}/layout.tsx`
    const coreLayoutPath = join(rootDir, layoutPath)
    const templateLayoutPath = join(templatesDir, `${currentPath}/layout.tsx`)

    // Check if core layout exists and template layout doesn't
    if (existsSync(coreLayoutPath) && !existsSync(templateLayoutPath)) {
      await duplicateLayoutWithTemplateResolver(coreLayoutPath, templateLayoutPath, layoutPath)
      layoutsGenerated++
      verbose(`Duplicated layout: ${layoutPath} → (templates)/${currentPath}/layout.tsx`)
    }
  }

  return layoutsGenerated
}

/**
 * Generate a single template page with template override system
 */
export async function generateTemplatePage(template, outputPath) {
  const { appPath, templateType, name, templatePath } = template

  // Create directory if it doesn't exist
  const dir = dirname(outputPath)
  await mkdir(dir, { recursive: true })

  // Determine the component name and content based on template type
  const routeSegments = name.split('/').filter(segment => !segment.startsWith('[') && !segment.endsWith(']'))
  const routeName = routeSegments[routeSegments.length - 1] || 'page'
  const componentName = `AutoGenerated${toPascalCase(routeName)}${toPascalCase(templateType)}`

  let content

  if (templateType === 'layout') {
    content = await generateLayoutPageContent(appPath, componentName, templatePath)
  } else {
    content = generateRegularPageContent(appPath, templatePath)
  }

  // Write the file
  await writeFile(outputPath, content, 'utf8')
  verbose(`Generated: ${outputPath.replace(rootDir, '')}`)
}

/**
 * Generate missing pages for templates with layout duplication
 * Creates pages in app/(templates)/ with necessary layouts duplicated
 * Only generates pages for templates that actually exist
 *
 * @param {Array} templates - List of template definitions
 * @param {Object} config - Configuration object with projectRoot
 */
export async function generateMissingPages(templates, config = null) {
  // Use config.projectRoot if provided, otherwise fall back to default rootDir
  if (config?.projectRoot) {
    rootDir = config.projectRoot
  }
  const templatesDir = join(rootDir, 'app', '(templates)')

  // First, clean up orphaned files before generating new ones
  await cleanupOrphanedTemplates(templates)

  if (templates.length === 0) {
    return
  }

  let generated = 0
  let layoutsDuplicated = 0

  // Track all unique directory paths that need layouts
  const layoutPathsNeeded = new Set()

  // Group templates by their directory path to identify needed layouts
  const templatesByPath = new Map()

  for (const template of templates) {
    const { appPath, templateType } = template
    const templatePagePath = join(rootDir, 'app', '(templates)', appPath.replace('app/', ''))

    // Track the directory path for layout generation
    layoutPathsNeeded.add(appPath)

    // Check if core page exists (skip generation if it does)
    // IMPORTANT: Always generate layouts, even if core layout exists
    const corePagePath = join(rootDir, appPath)
    if (existsSync(corePagePath) && templateType !== 'layout') {
      continue
    }

    // Track template for potential generation/update
    templatesByPath.set(appPath, { template, templatePagePath })
  }

  // Generate required layouts for ALL template page directories (even if layout override doesn't exist)
  // This ensures that pages in app/(templates)/ have the necessary layout hierarchy
  // Extract unique directories from all templates (not just layout templates)
  const uniqueDirectories = new Set()
  for (const template of templates) {
    const { appPath } = template
    // Extract directory path from page path
    const dirPath = appPath.replace(/\/[^\/]+$/, '')
    uniqueDirectories.add(dirPath)
  }

  for (const dirPath of uniqueDirectories) {
    const layoutsGenerated = await generateRequiredLayouts(`${dirPath}/page.tsx`, templatesDir)
    layoutsDuplicated += layoutsGenerated
  }

  // Generate or update template pages (regenerate if content differs - e.g., theme changed)
  let updated = 0
  for (const [appPath, { template, templatePagePath }] of templatesByPath) {
    const { templateType, name, templatePath } = template

    // Generate expected content
    const routeSegments = name.split('/').filter(segment => !segment.startsWith('[') && !segment.endsWith(']'))
    const routeName = routeSegments[routeSegments.length - 1] || 'page'
    const componentName = `AutoGenerated${toPascalCase(routeName)}${toPascalCase(templateType)}`

    let expectedContent
    if (templateType === 'layout') {
      expectedContent = await generateLayoutPageContent(appPath, componentName, templatePath)
    } else {
      expectedContent = generateRegularPageContent(appPath, templatePath)
    }

    // Check if file exists and compare content
    if (existsSync(templatePagePath)) {
      try {
        const currentContent = await readFile(templatePagePath, 'utf8')
        if (currentContent === expectedContent) {
          // Content matches, skip
          continue
        }
        // Content differs (theme changed), update file
        await writeFile(templatePagePath, expectedContent, 'utf8')
        updated++
        verbose(`Updated (theme changed): ${templatePagePath.replace(rootDir, '')}`)
      } catch (error) {
        // Error reading, regenerate
        await generateTemplatePage(template, templatePagePath)
        generated++
      }
    } else {
      // File doesn't exist, generate
      await generateTemplatePage(template, templatePagePath)
      generated++
    }
  }

  if (generated > 0 || updated > 0 || layoutsDuplicated > 0) {
    const parts = []
    if (generated > 0) parts.push(`${generated} new`)
    if (updated > 0) parts.push(`${updated} updated`)
    if (layoutsDuplicated > 0) parts.push(`${layoutsDuplicated} layouts`)
    log(`Template pages: ${parts.join(', ')}`, 'success')
  } else {
    verbose('All template pages up to date')
  }
}
