/**
 * Template Service
 * Manages deployment templates (PTF, Terraform, Helm, Ansible)
 */

import { getDb } from '../db/index.js'
import { logger } from '../lib/logger.js'
import { Context } from '../types/context.js'

export interface Template {
  id: string
  name: string
  slug: string
  description?: string
  templateType: TemplateType
  version: string
  content: string
  parameters: Record<string, any>
  outputs: Record<string, any>
  metadata: Record<string, any>
  status: TemplateStatus
  createdBy?: string
  createdAt: Date
  updatedAt: Date
}

export enum TemplateType {
  TERRAFORM = 'TERRAFORM',
  HELM = 'HELM',
  ANSIBLE = 'ANSIBLE',
  PTF = 'PTF',
  KUBERNETES = 'KUBERNETES',
}

export enum TemplateStatus {
  DRAFT = 'DRAFT',
  PUBLISHED = 'PUBLISHED',
  DEPRECATED = 'DEPRECATED',
}

export interface TemplateVersion {
  id: string
  templateId: string
  version: string
  content: string
  parameters: Record<string, any>
  outputs: Record<string, any>
  changelog?: string
  isLatest: boolean
  createdAt: Date
  updatedAt: Date
}

export interface TemplateFilter {
  templateType?: TemplateType
  status?: TemplateStatus
  search?: string
  limit?: number
  offset?: number
}

class TemplateService {
  /**
   * Get all templates
   */
  async getTemplates(context: Context, filter?: TemplateFilter): Promise<Template[]> {
    const db = getDb()
    const conditions: string[] = []
    const params: any[] = []
    let paramIndex = 1

    if (filter?.templateType) {
      conditions.push(`template_type = $${paramIndex}`)
      params.push(filter.templateType)
      paramIndex++
    }

    if (filter?.status) {
      conditions.push(`status = $${paramIndex}`)
      params.push(filter.status)
      paramIndex++
    } else if (context.user?.role !== 'ADMIN') {
      // Only show published templates to non-admins
      conditions.push(`status = 'PUBLISHED'`)
    }

    if (filter?.search) {
      conditions.push(
        `to_tsvector('english', coalesce(name, '') || ' ' || coalesce(description, '')) @@ plainto_tsquery('english', $${paramIndex})`
      )
      params.push(filter.search)
      paramIndex++
    }

    const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
    const limit = filter?.limit || 50
    const offset = filter?.offset || 0

    params.push(limit, offset)
    const limitClause = `LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`

    const query = `
      SELECT * FROM templates
      ${whereClause}
      ORDER BY created_at DESC
      ${limitClause}
    `

    const result = await db.query(query, params)
    return result.rows.map(this.mapTemplate)
  }

  /**
   * Get template by ID
   */
  async getTemplate(context: Context, id: string): Promise<Template | null> {
    const db = getDb()
    const result = await db.query(`SELECT * FROM templates WHERE id = $1`, [id])
    if (result.rows.length === 0) return null

    const template = this.mapTemplate(result.rows[0])

    // Check access for non-admins
    if (context.user?.role !== 'ADMIN' && template.status !== TemplateStatus.PUBLISHED) {
      return null
    }

    return template
  }

  /**
   * Get template by slug and version
   */
  async getTemplateBySlug(
    context: Context,
    slug: string,
    version?: string
  ): Promise<Template | null> {
    const db = getDb()
    let query = `SELECT * FROM templates WHERE slug = $1`
    const params: any[] = [slug]

    if (version) {
      query += ` AND version = $2`
      params.push(version)
    } else {
      query += ` ORDER BY created_at DESC LIMIT 1`
    }

    const result = await db.query(query, params)
    if (result.rows.length === 0) return null

    const template = this.mapTemplate(result.rows[0])

    // Check access for non-admins
    if (context.user?.role !== 'ADMIN' && template.status !== TemplateStatus.PUBLISHED) {
      return null
    }

    return template
  }

  /**
   * Create template
   */
  async createTemplate(
    context: Context,
    input: {
      name: string
      slug: string
      description?: string
      templateType: TemplateType
      version: string
      content: string
      parameters?: Record<string, any>
      outputs?: Record<string, any>
      metadata?: Record<string, any>
      status?: TemplateStatus
    }
  ): Promise<Template> {
    if (!context.user) {
      throw new Error('Authentication required')
    }

    const db = getDb()

    // Check if slug+version exists
    const existing = await db.query(
      `SELECT id FROM templates WHERE slug = $1 AND version = $2`,
      [input.slug, input.version]
    )
    if (existing.rows.length > 0) {
      throw new Error(`Template with slug "${input.slug}" and version "${input.version}" already exists`)
    }

    const result = await db.query(
      `INSERT INTO templates (
        name, slug, description, template_type, version, content,
        parameters, outputs, metadata, status, created_by
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
      RETURNING *`,
      [
        input.name,
        input.slug,
        input.description || null,
        input.templateType,
        input.version,
        input.content,
        JSON.stringify(input.parameters || {}),
        JSON.stringify(input.outputs || {}),
        JSON.stringify(input.metadata || {}),
        input.status || TemplateStatus.DRAFT,
        context.user.id,
      ]
    )

    logger.info('Template created', { templateId: result.rows[0].id })
    return this.mapTemplate(result.rows[0])
  }

  /**
   * Update template
   */
  async updateTemplate(
    context: Context,
    id: string,
    input: Partial<{
      name: string
      description: string
      content: string
      parameters: Record<string, any>
      outputs: Record<string, any>
      metadata: Record<string, any>
      status: TemplateStatus
    }>
  ): Promise<Template> {
    if (!context.user) {
      throw new Error('Authentication required')
    }

    const db = getDb()
    const updates: string[] = []
    const params: any[] = []
    let paramIndex = 1

    if (input.name !== undefined) {
      updates.push(`name = $${paramIndex}`)
      params.push(input.name)
      paramIndex++
    }

    if (input.description !== undefined) {
      updates.push(`description = $${paramIndex}`)
      params.push(input.description)
      paramIndex++
    }

    if (input.content !== undefined) {
      updates.push(`content = $${paramIndex}`)
      params.push(input.content)
      paramIndex++
    }

    if (input.parameters !== undefined) {
      updates.push(`parameters = $${paramIndex}`)
      params.push(JSON.stringify(input.parameters))
      paramIndex++
    }

    if (input.outputs !== undefined) {
      updates.push(`outputs = $${paramIndex}`)
      params.push(JSON.stringify(input.outputs))
      paramIndex++
    }

    if (input.metadata !== undefined) {
      updates.push(`metadata = $${paramIndex}`)
      params.push(JSON.stringify(input.metadata))
      paramIndex++
    }

    if (input.status !== undefined) {
      updates.push(`status = $${paramIndex}`)
      params.push(input.status)
      paramIndex++
    }

    if (updates.length === 0) {
      return this.getTemplate(context, id) as Promise<Template>
    }

    params.push(id)
    const result = await db.query(
      `UPDATE templates SET ${updates.join(', ')} WHERE id = $${paramIndex} RETURNING *`,
      params
    )

    logger.info('Template updated', { templateId: id })
    return this.mapTemplate(result.rows[0])
  }

  /**
   * Create template version
   */
  async createTemplateVersion(
    context: Context,
    input: {
      templateId: string
      version: string
      content: string
      parameters?: Record<string, any>
      outputs?: Record<string, any>
      changelog?: string
    }
  ): Promise<TemplateVersion> {
    if (!context.user) {
      throw new Error('Authentication required')
    }

    const db = getDb()

    // Check if version exists
    const existing = await db.query(
      `SELECT id FROM template_versions WHERE template_id = $1 AND version = $2`,
      [input.templateId, input.version]
    )
    if (existing.rows.length > 0) {
      throw new Error(`Version "${input.version}" already exists for this template`)
    }

    // If this is marked as latest, unmark other versions
    await db.query(
      `UPDATE template_versions SET is_latest = FALSE WHERE template_id = $1`,
      [input.templateId]
    )

    const result = await db.query(
      `INSERT INTO template_versions (
        template_id, version, content, parameters, outputs, changelog, is_latest
      ) VALUES ($1, $2, $3, $4, $5, $6, $7)
      RETURNING *`,
      [
        input.templateId,
        input.version,
        input.content,
        JSON.stringify(input.parameters || {}),
        JSON.stringify(input.outputs || {}),
        input.changelog || null,
        true, // Mark as latest
      ]
    )

    logger.info('Template version created', { versionId: result.rows[0].id })
    return this.mapTemplateVersion(result.rows[0])
  }

  /**
   * Get template versions
   */
  async getTemplateVersions(
    context: Context,
    templateId: string
  ): Promise<TemplateVersion[]> {
    const db = getDb()
    const result = await db.query(
      `SELECT * FROM template_versions WHERE template_id = $1 ORDER BY created_at DESC`,
      [templateId]
    )
    return result.rows.map(this.mapTemplateVersion)
  }

  // Mapper functions
  private mapTemplate(row: any): Template {
    return {
      id: row.id,
      name: row.name,
      slug: row.slug,
      description: row.description,
      templateType: row.template_type as TemplateType,
      version: row.version,
      content: row.content,
      parameters: row.parameters || {},
      outputs: row.outputs || {},
      metadata: row.metadata || {},
      status: row.status as TemplateStatus,
      createdBy: row.created_by,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    }
  }

  private mapTemplateVersion(row: any): TemplateVersion {
    return {
      id: row.id,
      templateId: row.template_id,
      version: row.version,
      content: row.content,
      parameters: row.parameters || {},
      outputs: row.outputs || {},
      changelog: row.changelog,
      isLatest: row.is_latest,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    }
  }
}

export const templateService = new TemplateService()

