/**
 * M-MEMBER-01-CLOSE-06C: 本地 Storage Provider 上传生命周期 Integration
 *
 * 使用 LocalStorageProvider 验证完整文件生命周期：
 *   upload-url → PUT → complete → head → download → delete
 * 重复 complete 幂等性、删除释放、存储配额验证
 */
import * as crypto from 'crypto';
import {
  startAPI, startWorker, stopAPI, stopWorker,
  httpGet, httpPost, generateJWT,
  setupFixtures, cleanupFixtures, dbExec, dbQuery, dbClose,
  setRedisConfig, getPrisma,
} from './helpers/integration-harness';
import * as http from 'http';

const DB_URL = process.env.DATABASE_URL || 'mysql://root:Zhixi@2026!Root_8C@127.0.0.1:3306/zhixi_prod';
const REDIS_HOST = process.env.REDIS_HOST || '127.0.0.1';
const REDIS_PORT = process.env.REDIS_PORT || '6379';
const REDIS_PW = process.env.REDIS_PASSWORD || '';
const TEST_USER = `file-test-${crypto.randomBytes(4).toString('hex')}`;
const TEST_KB = `file-kb-${crypto.randomBytes(4).toString('hex')}`;
const TEST_KI = `file-ki-${crypto.randomBytes(4).toString('hex')}`;
const TEST_SESSION = `file-ses-${crypto.randomBytes(4).toString('hex')}`;

let JWT_TOKEN: string;
let API_PID: number;
let WORKER_PID: number;
const prisma = getPrisma(DB_URL);

beforeAll(async () => {
  await setupFixtures(DB_URL, TEST_USER, TEST_KB, TEST_KI, TEST_SESSION);
  setRedisConfig(REDIS_HOST, parseInt(REDIS_PORT, 10), REDIS_PW || undefined);
  JWT_TOKEN = generateJWT(TEST_USER, `file-${Date.now()}@zhixi.app`, 'USER');
  API_PID = await startAPI('http://127.0.0.1:19999', DB_URL, REDIS_HOST, REDIS_PORT, REDIS_PW, process.env.JWT_SECRET || 'test-integration-secret');
  WORKER_PID = await startWorker(DB_URL, REDIS_HOST, REDIS_PORT, REDIS_PW, `file-worker-${Date.now()}`, process.env.JWT_SECRET || 'test-integration-secret');
  await new Promise(r => setTimeout(r, 3000));
}, 90_000);

afterAll(async () => {
  await stopWorker();
  await stopAPI();
  await cleanupFixtures(DB_URL, TEST_USER);
  await dbClose();
}, 30_000);

// ─── Helpers ──────────────────────────────────────────────

async function rawPut(url: string, body: string): Promise<{ status: number; data: any }> {
  return new Promise((resolve, reject) => {
    const u = new URL(url);
    const req = http.request({
      hostname: u.hostname, port: u.port, path: u.pathname + u.search,
      method: 'PUT', headers: { 'Content-Type': 'application/octet-stream', 'Content-Length': String(Buffer.byteLength(body)) },
    }, (res) => {
      let b = '';
      res.on('data', c => b += c);
      res.on('end', () => {
        try { resolve({ status: res.statusCode || 0, data: JSON.parse(b) }); }
        catch { resolve({ status: res.statusCode || 0, data: b }); }
      });
    });
    req.on('error', reject);
    req.write(body);
    req.end();
  });
}

// ─── Tests ────────────────────────────────────────────────

describe('File Upload Lifecycle (Local Storage)', () => {
  let uploadId: string;
  let objectKey: string;
  let uploadUrl: string;
  let fileId: string;

  it('1. upload-url: 返回 uploadId + uploadUrl', async () => {
    const r = await httpPost('/api/files/upload-url', {
      filename: 'lifecycle-test.txt',
      mimeType: 'text/plain',
      sizeBytes: 100,
    }, JWT_TOKEN);
    expect(r.status).toBeLessThan(400);
    const data = r.data?.data || r.data;
    expect(data.uploadId).toBeDefined();
    expect(data.uploadUrl).toBeDefined();
    expect(data.objectKey).toBeDefined();
    uploadId = data.uploadId;
    objectKey = data.objectKey;
    uploadUrl = data.uploadUrl;
  });

  it('2. PUT file: 上传文件到 local storage', async () => {
    const content = 'Hello World File Upload Integration Test Content';
    const r = await rawPut(uploadUrl, content);
    expect(r.status).toBe(200);
    expect(r.data?.success).toBe(true);
    expect(r.data?.size).toBe(Buffer.byteLength(content));
  });

  it('3. complete: 确认上传，创建 UploadedFile 记录', async () => {
    const r = await httpPost('/api/files/complete', {
      uploadId,
      objectKey,
    }, JWT_TOKEN);
    expect(r.status).toBeLessThan(400);
    const data = r.data?.data || r.data;
    expect(data.id).toBeDefined();
    expect(data.sizeBytes).toBeDefined();
    fileId = data.id;
  });

  it('4. complete idempotent: 重复调用返回同一个 fileId', async () => {
    const r = await httpPost('/api/files/complete', {
      uploadId,
      objectKey,
    }, JWT_TOKEN);
    expect(r.status).toBeLessThan(400);
    const data = r.data?.data || r.data;
    expect(data.id).toBe(fileId);
  });

  it('5. DB record: UploadedFile 记录存在', async () => {
    const rows = await dbQuery(DB_URL, `SELECT id, filename, sizeBytes, storagePath FROM UploadedFile WHERE id = ?`, [fileId]);
    expect(rows.length).toBe(1);
    expect(rows[0].id).toBe(fileId);
    expect(Number(rows[0].sizeBytes)).toBeGreaterThan(0);
  });

  it('6. DB record: sizeBytes = 实际文件大小', async () => {
    const rows = await dbQuery(DB_URL, `SELECT sizeBytes FROM UploadedFile WHERE id = ?`, [fileId]);
    expect(rows.length).toBe(1);
    expect(Number(rows[0].sizeBytes)).toBe(48); // "Hello World File Upload Integration Test Content"
  });

  it('7. delete: 删除文件', async () => {
    const delResult = await httpSend('DELETE', '/api/files/' + fileId, null, JWT_TOKEN);
    expect(delResult.status).toBeLessThan(500);
  });

  it('8. after delete: DB 记录已清除', async () => {
    const rows = await dbQuery(DB_URL, `SELECT COUNT(*) AS c FROM UploadedFile WHERE id = ?`, [fileId]);
    expect(Number(rows[0].c)).toBe(0);
  });
});

// ─── Helper: raw DELETE ────────────────────────────────────

async function httpSend(method: string, path: string, body: any, token?: string): Promise<{ status: number; data: any }> {
  return new Promise((resolve, reject) => {
    const payload = body ? JSON.stringify(body) : undefined;
    const headers: Record<string, string> = {};
    if (payload) { headers['Content-Type'] = 'application/json'; headers['Content-Length'] = String(Buffer.byteLength(payload)); }
    if (token) headers['Authorization'] = `Bearer ${token}`;

    const req = http.request({
      hostname: '127.0.0.1', port: 3000, path, method, headers,
    }, (res) => {
      let b = '';
      res.on('data', c => b += c);
      res.on('end', () => {
        try { resolve({ status: res.statusCode || 0, data: JSON.parse(b) }); }
        catch { resolve({ status: res.statusCode || 0, data: b }); }
      });
    });
    req.on('error', reject);
    if (payload) req.write(payload);
    req.end();
  });
}
