/**
 * Quartz Sync Integration
 * Provides multiple approaches for syncing notes back to Quartz sites
 */

export interface QuartzSyncConfig {
  githubToken?: string
  githubRepo?: string
  quartzUrl?: string
  cloudflareApiKey?: string
  cloudflareAccountId?: string
}

export interface QuartzNote {
  id: string
  title: string
  content: string
  tags: string[]
  frontmatter: Record<string, any>
  filePath: string
  lastModified: Date
}

export class QuartzSync {
  private config: QuartzSyncConfig

  constructor(config: QuartzSyncConfig) {
    this.config = config
  }

  /**
   * Approach 1: GitHub API Integration
   * Sync directly to the GitHub repository that powers the Quartz site
   */
  async syncToGitHub(note: QuartzNote): Promise<boolean> {
    if (!this.config.githubToken || !this.config.githubRepo) {
      throw new Error('GitHub token and repository required for GitHub sync')
    }

    try {
      const { githubToken, githubRepo } = this.config
      const [owner, repo] = githubRepo.split('/')
      
      console.log('🔧 GitHub sync details:', {
        owner,
        repo,
        noteTitle: note.title,
        noteFilePath: note.filePath
      })
      
      // Get the current file content to check if it exists
      const filePath = `content/${note.filePath}`
      let sha: string | undefined
      
      console.log('🔍 Checking for existing file:', filePath)
      
      try {
        const apiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`
        console.log('🌐 Making API call to:', apiUrl)
        
        const existingFile = await fetch(apiUrl, {
          headers: {
            'Authorization': `token ${githubToken}`,
            'Accept': 'application/vnd.github.v3+json'
          }
        })
        
        console.log('📡 API response status:', existingFile.status)
        
        if (existingFile.ok) {
          const fileData = await existingFile.json() as { sha: string }
          sha = fileData.sha
          console.log('✅ File exists, will update with SHA:', sha)
        } else {
          console.log('ℹ️ File does not exist, will create new one')
        }
      } catch (error) {
        // File doesn't exist, that's okay
        console.log('ℹ️ File does not exist, will create new one:', error)
      }

      // Create the markdown content
      const frontmatter = Object.entries(note.frontmatter)
        .map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
        .join('\n')
      
      const content = `---
${frontmatter}
---

${note.content}`

      // Encode content to base64
      const encodedContent = btoa(unescape(encodeURIComponent(content)))

      // Create or update the file
      const response = await fetch(
        `https://api.github.com/repos/${owner}/${repo}/contents/${filePath}`,
        {
          method: 'PUT',
          headers: {
            'Authorization': `token ${githubToken}`,
            'Accept': 'application/vnd.github.v3+json',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            message: `Update note: ${note.title}`,
            content: encodedContent,
            ...(sha && { sha }) // Include SHA if updating existing file
          })
        }
      )

      if (response.ok) {
        const result = await response.json() as { commit: { sha: string } }
        console.log('✅ Successfully synced note to GitHub:', note.title)
        console.log('📁 File path:', filePath)
        console.log('🔗 Commit SHA:', result.commit.sha)
        return true
      } else {
        const error = await response.text()
        let errorMessage = `GitHub API error: ${response.status}`
        
        try {
          const errorData = JSON.parse(error)
          if (errorData.message) {
            errorMessage += ` - ${errorData.message}`
          }
        } catch (e) {
          errorMessage += ` - ${error}`
        }
        
        throw new Error(errorMessage)
      }
    } catch (error) {
      console.error('❌ Failed to sync to GitHub:', error)
      throw error
    }
  }

  /**
   * Approach 2: Cloudflare R2 + Durable Objects
   * Use the existing Cloudflare infrastructure for persistent storage
   */
  async syncToCloudflare(note: QuartzNote): Promise<boolean> {
    if (!this.config.cloudflareApiKey || !this.config.cloudflareAccountId) {
      throw new Error('Cloudflare credentials required for Cloudflare sync')
    }

    try {
      // Store in Cloudflare R2
      const response = await fetch('/api/quartz/sync', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.config.cloudflareApiKey}`
        },
        body: JSON.stringify({
          note,
          accountId: this.config.cloudflareAccountId
        })
      })

      if (response.ok) {
        console.log('✅ Successfully synced note to Cloudflare:', note.title)
        return true
      } else {
        throw new Error(`Cloudflare sync failed: ${response.statusText}`)
      }
    } catch (error) {
      console.error('❌ Failed to sync to Cloudflare:', error)
      throw error
    }
  }

  /**
   * Approach 3: Direct Quartz API (if available)
   * Some Quartz sites may expose APIs for content updates
   */
  async syncToQuartzAPI(note: QuartzNote): Promise<boolean> {
    if (!this.config.quartzUrl) {
      throw new Error('Quartz URL required for API sync')
    }

    try {
      const response = await fetch(`${this.config.quartzUrl}/api/notes`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(note)
      })

      if (response.ok) {
        console.log('✅ Successfully synced note to Quartz API:', note.title)
        return true
      } else {
        throw new Error(`Quartz API error: ${response.statusText}`)
      }
    } catch (error) {
      console.error('❌ Failed to sync to Quartz API:', error)
      throw error
    }
  }

  /**
   * Approach 4: Webhook Integration
   * Send updates to a webhook that can process and sync to Quartz
   */
  async syncViaWebhook(note: QuartzNote, webhookUrl: string): Promise<boolean> {
    try {
      const response = await fetch(webhookUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          type: 'note_update',
          note,
          timestamp: new Date().toISOString()
        })
      })

      if (response.ok) {
        console.log('✅ Successfully sent note to webhook:', note.title)
        return true
      } else {
        throw new Error(`Webhook error: ${response.statusText}`)
      }
    } catch (error) {
      console.error('❌ Failed to sync via webhook:', error)
      throw error
    }
  }

  /**
   * Smart sync - tries multiple approaches in order of preference
   * Prioritizes GitHub integration for Quartz sites
   */
  async smartSync(note: QuartzNote): Promise<boolean> {
    console.log('🔄 Starting smart sync for note:', note.title)
    console.log('🔧 Sync config available:', {
      hasGitHubToken: !!this.config.githubToken,
      hasGitHubRepo: !!this.config.githubRepo,
      hasCloudflareApiKey: !!this.config.cloudflareApiKey,
      hasCloudflareAccountId: !!this.config.cloudflareAccountId,
      hasQuartzUrl: !!this.config.quartzUrl
    })
    
    // Check if GitHub integration is available and preferred
    if (this.config.githubToken && this.config.githubRepo) {
      try {
        console.log('🔄 Attempting GitHub sync (preferred method)')
        const result = await this.syncToGitHub(note)
        if (result) {
          console.log('✅ GitHub sync successful!')
          return true
        }
      } catch (error) {
        console.warn('⚠️ GitHub sync failed, trying other methods:', error)
        console.warn('⚠️ GitHub sync error details:', {
          message: error instanceof Error ? error.message : 'Unknown error',
          stack: error instanceof Error ? error.stack : 'No stack trace'
        })
      }
    } else {
      console.log('⚠️ GitHub sync not available - missing token or repo')
    }

    // Fallback to other methods
    const fallbackMethods = [
      () => this.syncToCloudflare(note),
      () => this.syncToQuartzAPI(note)
    ]

    for (const syncMethod of fallbackMethods) {
      try {
        const result = await syncMethod()
        if (result) return true
      } catch (error) {
        console.warn('Sync method failed, trying next:', error)
        continue
      }
    }

    throw new Error('All sync methods failed')
  }
}

/**
 * Utility function to create a Quartz note from an ObsNote shape
 */
export function createQuartzNoteFromShape(shape: any): QuartzNote {
  const title = shape.props.title || 'Untitled'
  const content = shape.props.content || ''
  const tags = shape.props.tags || []
  
  // Use stored filePath if available to maintain filename consistency
  // Otherwise, generate from title
  let filePath: string
  if (shape.props.filePath && shape.props.filePath.trim() !== '') {
    filePath = shape.props.filePath
    // Ensure it ends with .md if it doesn't already
    if (!filePath.endsWith('.md')) {
      filePath = filePath.endsWith('/') ? `${filePath}${title}.md` : `${filePath}.md`
    }
  } else {
    // Generate from title, ensuring it's a valid filename
    const sanitizedTitle = title.replace(/[^a-zA-Z0-9\s-]/g, '').trim().replace(/\s+/g, '-')
    filePath = `${sanitizedTitle}.md`
  }
  
  return {
    id: shape.props.noteId || title,
    title,
    content,
    tags: tags.map((tag: string) => tag.replace('#', '')),
    frontmatter: {
      title: title,
      tags: tags.map((tag: string) => tag.replace('#', '')),
      created: new Date().toISOString(),
      modified: new Date().toISOString()
    },
    filePath: filePath,
    lastModified: new Date()
  }
}
