import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model, Types } from 'mongoose'
import { execFile } from 'child_process'
import { promisify } from 'util'
import { promises as fs } from 'fs'
import * as path from 'path'
import Anthropic from '@anthropic-ai/sdk'
import { LegalDocument, LegalDocumentDocument, ClientFolder, ClientFolderDocument } from '../common/schemas'
import { CreateDocumentDto } from './dto/create-document.dto'
import { UpdateDocumentDto } from './dto/update-document.dto'

const execFileAsync = promisify(execFile)

@Injectable()
export class DocumentsService {
  private readonly anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })
  private readonly uploadsRoot = process.env.UPLOADS_DIR ?? path.join(process.cwd(), 'uploads')

  constructor(
    @InjectModel(LegalDocument.name) private docModel: Model<LegalDocumentDocument>,
    @InjectModel(ClientFolder.name) private folderModel: Model<ClientFolderDocument>,
  ) {}

  async create(dto: CreateDocumentDto): Promise<LegalDocumentDocument> {
    const doc = new this.docModel({ ...dto, version: dto.version ?? '1.0', signed: dto.signed ?? false })
    return doc.save()
  }

  async findAll(filters: {
    clientFolderId?: string
    matterId?: string
    privilege?: string
    documentType?: string
    limit?: number
    offset?: number
  }): Promise<{ items: LegalDocumentDocument[]; total: number }> {
    const query: any = {}
    if (filters.clientFolderId) query.clientFolderId = new Types.ObjectId(filters.clientFolderId)
    if (filters.matterId) query.matterId = new Types.ObjectId(filters.matterId)
    if (filters.privilege) query.privilege = filters.privilege
    if (filters.documentType) query.documentType = filters.documentType

    const [items, total] = await Promise.all([
      this.docModel.find(query)
        .sort({ createdAt: -1 })
        .skip(filters.offset ?? 0)
        .limit(filters.limit ?? 50)
        .exec(),
      this.docModel.countDocuments(query),
    ])
    return { items, total }
  }

  async findOne(id: string): Promise<LegalDocumentDocument> {
    const doc = await this.docModel.findById(id).exec()
    if (!doc) throw new NotFoundException(`Document ${id} not found`)
    return doc
  }

  async update(id: string, dto: UpdateDocumentDto): Promise<LegalDocumentDocument> {
    const doc = await this.docModel.findByIdAndUpdate(id, dto, { new: true }).exec()
    if (!doc) throw new NotFoundException(`Document ${id} not found`)
    return doc as LegalDocumentDocument
  }

  // ── Text extraction ────────────────────────────────────────────────────────

  private async extractText(storageKey: string, mimeType?: string): Promise<string | null> {
    const filePath = path.join(this.uploadsRoot, storageKey)
    const mime = mimeType ?? ''

    if (mime.includes('pdf') || storageKey.endsWith('.pdf')) {
      try {
        const { stdout } = await execFileAsync('pdftotext', ['-layout', filePath, '-'])
        return stdout.slice(0, 60_000)
      } catch {
        return null
      }
    }

    if (mime.includes('text') || /\.(txt|md|csv)$/i.test(storageKey)) {
      try {
        const buf = await fs.readFile(filePath, 'utf-8')
        return buf.slice(0, 60_000)
      } catch {
        return null
      }
    }

    return null // Word, images etc — fall back to summary-only translation
  }

  // ── Translate ──────────────────────────────────────────────────────────────

  async translate(id: string): Promise<{
    translatedText: string | null
    translatedSummary: string | null
    sourceLang: string
    targetLang: string
    hasFullText: boolean
  }> {
    const doc = await this.findOne(id)
    const sourceLang  = doc.detectedLanguage === 'th' ? 'th' : 'en'
    const targetLang  = sourceLang === 'th' ? 'en' : 'th'
    const targetLabel = targetLang === 'th' ? 'Thai' : 'English'
    const sourceLabel = sourceLang === 'th' ? 'Thai' : 'English'

    const system = `You are a professional legal translator specialising in Thai and English legal documents.
Translate accurately and preserve all legal terminology, proper nouns, reference numbers, dates, and formatting.
Output only the translation — no commentary, no explanations.`

    const rawText = await this.extractText(doc.storageKey, doc.mimeType)
    let translatedText: string | null = null
    let translatedSummary: string | null = null

    if (rawText) {
      const msg = await this.anthropic.messages.create({
        model: 'claude-sonnet-4-6',
        max_tokens: 8096,
        system,
        messages: [{
          role: 'user',
          content: `Translate the following ${sourceLabel} legal document to ${targetLabel}:\n\n${rawText}`,
        }],
      })
      translatedText = (msg.content[0] as any).text ?? null
    }

    if (doc.summary) {
      const msg = await this.anthropic.messages.create({
        model: 'claude-sonnet-4-6',
        max_tokens: 1024,
        system,
        messages: [{
          role: 'user',
          content: `Translate this legal document summary from ${sourceLabel} to ${targetLabel}:\n\n${doc.summary}`,
        }],
      })
      translatedSummary = (msg.content[0] as any).text ?? null

      if (translatedSummary) {
        await this.docModel.findByIdAndUpdate(id, {
          translatedSummary,
          translatedSummaryLang: targetLang,
        }).exec()
      }
    }

    return { translatedText, translatedSummary, sourceLang, targetLang, hasFullText: !!rawText }
  }

  // ── Save translation as a new document in the same client folder ───────────

  async saveTranslation(
    sourceId: string,
    translatedText: string,
    targetLang: string,
  ): Promise<LegalDocumentDocument> {
    const source   = await this.findOne(sourceId)
    const folderId = source.clientFolderId.toString()

    const updated = await this.folderModel.findByIdAndUpdate(
      folderId,
      { $inc: { batesCounter: 1 } },
      { new: true },
    ).exec()
    if (!updated) throw new BadRequestException(`Client folder ${folderId} not found`)

    const seq         = updated.batesCounter
    const batesNumber = `${updated.folderNumber}-${String(seq).padStart(5, '0')}`
    const fileName    = `${batesNumber}.txt`
    const storageKey  = path.join(folderId, fileName)
    const dest        = path.join(this.uploadsRoot, storageKey)

    await fs.mkdir(path.dirname(dest), { recursive: true })
    await fs.writeFile(dest, translatedText, 'utf-8')

    const langLabel = targetLang === 'th' ? 'Thai' : 'English'
    const newDoc = await this.docModel.create({
      clientFolderId:    source.clientFolderId,
      matterId:          source.matterId,
      batesNumber,
      originalName:      fileName,
      fileName,
      storageKey,
      mimeType:          'text/plain',
      fileSize:          Buffer.byteLength(translatedText, 'utf-8'),
      title:             `[${langLabel} Translation] ${source.title}`,
      documentType:      source.documentType ?? 'other',
      privilege:         source.privilege,
      aiStatus:          'classified',
      classified:        true,
      summary:           source.translatedSummary ?? undefined,
      detectedLanguage:  targetLang,
      tags:              [...(source.tags ?? []), 'translation'],
      parties:           source.parties ?? [],
      uploadedBy:        'translation-agent',
      isTranslation:     true,
      translationLanguage: targetLang,
      sourceDocumentId:  source._id as Types.ObjectId,
    })

    return newDoc
  }
}
