/**
 * Movement-file generator — accountant-export (wave-15).
 *
 * Generates a Hashavshevet-compatible movement file (CP1255-encoded)
 * from the derived ledger movements for the given period.
 *
 * File format:
 *  One record per movement leg, based on the B100/B110 ITA uniform structure.
 *  Uses the same CP1255 encoding, agorot, and date conventions as spec 180.
 *
 * Output: written to R2 at key exports/accountant/{tenantId}/{jobId}_movements.txt
 */
import { eq, and } from '@zync/db'
import { createDb, accountantExportJobs } from '@zync/db/queries'
import { encodeCP1255 } from '../lib/cp1255'
import { deriveMovements } from './movements'
import type { Env } from '@zync/types'
import type { Movement } from '@zync/types'

type R2BucketWithPresign = R2Bucket & {
  createPresignedUrl?: (
    method: string,
    key: string,
    options: { expiresIn: number },
  ) => Promise<string>
}

async function _presignR2Url(env: Env, r2Key: string, expiresIn: number): Promise<string> {
  const storage = env.STORAGE as R2BucketWithPresign
  if (typeof storage.createPresignedUrl === 'function') {
    return storage.createPresignedUrl('GET', r2Key, { expiresIn })
  }
  const { AwsClient } = await import('aws4fetch')
  const envAny = env as unknown as Record<string, string>
  const accountId = envAny['CF_ACCOUNT_ID'] ?? ''
  const accessKeyId = envAny['R2_ACCESS_KEY_ID'] ?? ''
  const secretAccessKey = envAny['R2_SECRET_ACCESS_KEY'] ?? ''
  const bucketName = envAny['R2_BUCKET_NAME'] ?? 'zync-storage'
  const endpoint = `https://${accountId}.r2.cloudflarestorage.com`
  const aws = new AwsClient({ accessKeyId, secretAccessKey, region: 'auto', service: 's3' })
  const url = new URL(`${endpoint}/${bucketName}/${r2Key}`)
  url.searchParams.set('X-Amz-Expires', String(expiresIn))
  const presigned = await aws.sign(new Request(url.toString(), { method: 'GET' }), { aws: { signQuery: true } })
  return presigned.url
}

// ── Record formatting ─────────────────────────────────────────────────────────

function numField(value: number, width: number): string {
  const s = Math.abs(Math.round(value)).toString()
  return s.padStart(width, '0').slice(-width)
}

function agorot(amountIls: number): number {
  return Math.round(amountIls * 100)
}

function dateField(d: string): string {
  return d.replace(/-/g, '').slice(0, 8)
}

function textField(value: string | null | undefined, width: number): string {
  const s = (value ?? '').slice(0, width)
  return s.padEnd(width, ' ')
}

/**
 * Build one B100 movement record line.
 * Layout based on ITA B100 journal movement record:
 *  1-4:   'B100'
 *  5-12:  Date YYYYMMDD
 *  13-20: Debit account (8 chars, left-padded)
 *  21-28: Credit account (8 chars, left-padded)
 *  29-42: Amount in agorot (14 digits, zero-padded)
 *  43-72: Reference (30 chars)
 *  73-112: Description (40 chars)
 */
function buildB100(m: Movement, lineNo: number): string {
  return [
    'B100',
    dateField(m.date),
    textField(m.debitAccount, 8),
    textField(m.creditAccount, 8),
    numField(agorot(m.amountIls), 14),
    textField(m.reference, 30),
    textField(m.description, 40),
    numField(lineNo, 9),
  ].join('')
}

// ── Generator ─────────────────────────────────────────────────────────────────

/**
 * Generate Hashavshevet movement file and upload to R2.
 * Updates accountant_export_jobs row status pending → running → done|error.
 */
export async function generateMovementFile(
  env: Env,
  tenantId: string,
  periodFrom: string,
  periodTo: string,
  jobId: string,
  format: 'hashavshevet' = 'hashavshevet',
): Promise<void> {
  if (format !== 'hashavshevet') {
    throw new Error(`Unsupported movement file format: ${format}`)
  }
  const db = createDb(env)

  // Mark running
  await db
    .update(accountantExportJobs)
    .set({ status: 'running' })
    .where(and(eq(accountantExportJobs.id, jobId), eq(accountantExportJobs.tenantId, tenantId)))

  try {
    const movements = await deriveMovements(env, tenantId, periodFrom, periodTo)

    // Build file content: one B100 line per movement
    const lines: Uint8Array[] = []
    let lineNo = 1
    for (const m of movements) {
      const line = buildB100(m, lineNo++)
      // Encode CP1255, append CRLF
      const encoded = encodeCP1255(line + '\r\n')
      lines.push(encoded)
    }

    // Concatenate all lines into a single buffer
    const totalBytes = lines.reduce((n, l) => n + l.length, 0)
    const buf = new Uint8Array(totalBytes)
    let offset = 0
    for (const l of lines) {
      buf.set(l, offset)
      offset += l.length
    }

    const r2Key = `exports/accountant/${tenantId}/${jobId}_movements.txt`
    await (env.STORAGE as R2Bucket).put(r2Key, buf, {
      httpMetadata: { contentType: 'text/plain' },
    })

    const downloadExpiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000)
    await db
      .update(accountantExportJobs)
      .set({
        status: 'done',
        r2Key,
        downloadExpiresAt,
      })
      .where(and(eq(accountantExportJobs.id, jobId), eq(accountantExportJobs.tenantId, tenantId)))
  } catch (err) {
    const errorMessage = err instanceof Error ? err.message : String(err)
    await db
      .update(accountantExportJobs)
      .set({ status: 'error' })
      .where(and(eq(accountantExportJobs.id, jobId), eq(accountantExportJobs.tenantId, tenantId)))
    throw new Error(`generateMovementFile failed: ${errorMessage}`)
  }
}
