/**
 * Outlook Service
 * Handles Microsoft Graph API operations for Outlook mail
 */

import { log } from '../middleware/logger.mjs'

const GRAPH_API_BASE = 'https://graph.microsoft.com/v1.0'

/**
 * Outlook Service Class
 */
export class OutlookService {
  constructor(accessToken) {
    this.accessToken = accessToken
    this.headers = {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    }
  }

  /**
   * Make Graph API request
   * @private
   */
  async _request(endpoint, options = {}) {
    const url = endpoint.startsWith('http') ? endpoint : `${GRAPH_API_BASE}${endpoint}`

    const response = await fetch(url, {
      ...options,
      headers: {
        ...this.headers,
        ...options.headers,
      },
    })

    if (!response.ok) {
      const error = await response.json().catch(() => ({}))
      throw new Error(error.error?.message || `Graph API Error: ${response.status}`)
    }

    // Handle 204 No Content
    if (response.status === 204) {
      return null
    }

    return response.json()
  }

  /**
   * Get user profile
   */
  async getProfile() {
    return this._request('/me')
  }

  /**
   * List emails from inbox
   * @param {number} top - Number of emails to fetch
   * @param {string} skip - Skip token for pagination
   * @param {string} filter - OData filter
   */
  async listEmails(top = 50, skip = null, filter = null) {
    let endpoint = `/me/mailFolders/inbox/messages?$top=${top}&$select=id,subject,from,bodyPreview,receivedDateTime,isRead,categories`

    if (filter) {
      endpoint += `&$filter=${encodeURIComponent(filter)}`
    }

    if (skip) {
      endpoint += `&$skip=${skip}`
    }

    const data = await this._request(endpoint)

    return {
      messages: data.value || [],
      nextLink: data['@odata.nextLink'],
      count: data['@odata.count'],
    }
  }

  /**
   * Get full email details
   * @param {string} messageId - Message ID
   */
  async getEmail(messageId) {
    return this._request(`/me/messages/${messageId}?$select=id,subject,from,body,bodyPreview,receivedDateTime,isRead,categories,importance,flag`)
  }

  /**
   * Batch get multiple emails
   * @param {string[]} messageIds - Array of message IDs
   */
  async batchGetEmails(messageIds) {
    // Use Graph batch API for efficiency
    const batchRequest = {
      requests: messageIds.map((id, index) => ({
        id: String(index),
        method: 'GET',
        url: `/me/messages/${id}?$select=id,subject,from,bodyPreview,receivedDateTime,categories`,
      })),
    }

    const response = await this._request('/$batch', {
      method: 'POST',
      body: JSON.stringify(batchRequest),
    })

    return response.responses
      .filter(r => r.status === 200)
      .map(r => r.body)
  }

  /**
   * Update message properties
   * @param {string} messageId - Message ID
   * @param {object} updates - Properties to update
   */
  async updateMessage(messageId, updates) {
    return this._request(`/me/messages/${messageId}`, {
      method: 'PATCH',
      body: JSON.stringify(updates),
    })
  }

  /**
   * Add categories to a message
   * @param {string} messageId - Message ID
   * @param {string[]} categories - Categories to add
   */
  async addCategories(messageId, categories) {
    const email = await this.getEmail(messageId)
    const existingCategories = email.categories || []
    const newCategories = [...new Set([...existingCategories, ...categories])]

    return this.updateMessage(messageId, { categories: newCategories })
  }

  /**
   * Remove categories from a message
   * @param {string} messageId - Message ID
   * @param {string[]} categories - Categories to remove
   */
  async removeCategories(messageId, categories) {
    const email = await this.getEmail(messageId)
    const existingCategories = email.categories || []
    const newCategories = existingCategories.filter(c => !categories.includes(c))

    return this.updateMessage(messageId, { categories: newCategories })
  }

  /**
   * Archive a message (move to archive folder)
   * @param {string} messageId - Message ID
   */
  async archiveEmail(messageId) {
    // First, try to get or create archive folder
    let archiveFolder
    try {
      archiveFolder = await this._request('/me/mailFolders/archive')
    } catch {
      // Archive folder might not exist, try to find it
      const folders = await this._request('/me/mailFolders?$filter=displayName eq \'Archive\'')
      if (folders.value?.length) {
        archiveFolder = folders.value[0]
      }
    }

    if (archiveFolder) {
      return this._request(`/me/messages/${messageId}/move`, {
        method: 'POST',
        body: JSON.stringify({ destinationId: archiveFolder.id }),
      })
    }

    // Fallback: just mark as read
    return this.markAsRead(messageId)
  }

  /**
   * Move message to deleted items
   * @param {string} messageId - Message ID
   */
  async deleteEmail(messageId) {
    return this._request(`/me/messages/${messageId}/move`, {
      method: 'POST',
      body: JSON.stringify({ destinationId: 'deleteditems' }),
    })
  }

  /**
   * Move message to a folder
   * @param {string} messageId - Message ID
   * @param {string} folderId - Destination folder ID
   */
  async moveEmail(messageId, folderId) {
    return this._request(`/me/messages/${messageId}/move`, {
      method: 'POST',
      body: JSON.stringify({ destinationId: folderId }),
    })
  }

  /**
   * Mark message as read
   * @param {string} messageId - Message ID
   */
  async markAsRead(messageId) {
    return this.updateMessage(messageId, { isRead: true })
  }

  /**
   * Mark message as unread
   * @param {string} messageId - Message ID
   */
  async markAsUnread(messageId) {
    return this.updateMessage(messageId, { isRead: false })
  }

  /**
   * Flag a message
   * @param {string} messageId - Message ID
   * @param {string} flagStatus - 'flagged' | 'complete' | 'notFlagged'
   */
  async flagEmail(messageId, flagStatus = 'flagged') {
    return this.updateMessage(messageId, {
      flag: { flagStatus },
    })
  }

  /**
   * Set message importance
   * @param {string} messageId - Message ID
   * @param {string} importance - 'low' | 'normal' | 'high'
   */
  async setImportance(messageId, importance) {
    return this.updateMessage(messageId, { importance })
  }

  /**
   * Get all mail folders
   */
  async getFolders() {
    const data = await this._request('/me/mailFolders')
    return data.value || []
  }

  /**
   * Create a mail folder
   * @param {string} displayName - Folder name
   * @param {string} parentFolderId - Parent folder ID (optional)
   */
  async createFolder(displayName, parentFolderId = null) {
    const endpoint = parentFolderId
      ? `/me/mailFolders/${parentFolderId}/childFolders`
      : '/me/mailFolders'

    return this._request(endpoint, {
      method: 'POST',
      body: JSON.stringify({ displayName }),
    })
  }

  /**
   * Get available categories
   */
  async getCategories() {
    const data = await this._request('/me/outlook/masterCategories')
    return data.value || []
  }

  /**
   * Create a category
   * @param {string} displayName - Category name
   * @param {string} color - Color preset (e.g., 'preset0' to 'preset24')
   */
  async createCategory(displayName, color = 'preset0') {
    try {
      return await this._request('/me/outlook/masterCategories', {
        method: 'POST',
        body: JSON.stringify({ displayName, color }),
      })
    } catch (error) {
      // Category might already exist
      log.warn(`Kategorie erstellen fehlgeschlagen: ${displayName}`, { error: error.message })
      return null
    }
  }

  /**
   * Create subscription for webhook notifications
   * @param {string} webhookUrl - Notification URL
   * @param {number} expirationMinutes - Subscription expiration (max 4230 minutes / ~3 days)
   */
  async createSubscription(webhookUrl, expirationMinutes = 4230) {
    const expirationDateTime = new Date(Date.now() + expirationMinutes * 60000).toISOString()

    return this._request('/subscriptions', {
      method: 'POST',
      body: JSON.stringify({
        changeType: 'created,updated',
        notificationUrl: webhookUrl,
        resource: 'me/mailFolders(\'inbox\')/messages',
        expirationDateTime,
        clientState: 'mailflow-webhook',
      }),
    })
  }

  /**
   * Renew subscription
   * @param {string} subscriptionId - Subscription ID
   * @param {number} expirationMinutes - New expiration time
   */
  async renewSubscription(subscriptionId, expirationMinutes = 4230) {
    const expirationDateTime = new Date(Date.now() + expirationMinutes * 60000).toISOString()

    return this._request(`/subscriptions/${subscriptionId}`, {
      method: 'PATCH',
      body: JSON.stringify({ expirationDateTime }),
    })
  }

  /**
   * Delete subscription
   * @param {string} subscriptionId - Subscription ID
   */
  async deleteSubscription(subscriptionId) {
    return this._request(`/subscriptions/${subscriptionId}`, {
      method: 'DELETE',
    })
  }
}

export default OutlookService
