/**
 * Auth API Integration Tests
 * TDD: Test-Driven Development
 */

import { describe, it, expect, afterEach, beforeEach, beforeAll, afterAll } from '@jest/globals';
import request from 'supertest';
import { app } from '../../../src/index';
import { prisma } from '../../../src/lib/prisma';
import { PasswordService } from '../../../src/services/password.service';
import { AuthService } from '../../../src/services/auth.service';

describe('Auth API Integration Tests', () => {
  // @ralph 测试隔离是否充分？每个测试后清理数据

  beforeAll(async () => {
    // Ensure connection is established
    await prisma.$connect();
  });

  afterEach(async () => {
    // Clean up test data
    await prisma.user.deleteMany({});
  });

  afterAll(async () => {
    await prisma.$disconnect();
  });

  describe('POST /api/auth/register', () => {
    const validUser = {
      username: 'testuser',
      email: 'test@example.com',
      password: 'SecurePass123!'
    };

    it('should register new user successfully', async () => {
      // @ralph 这个测试是否覆盖了成功路径？
      const response = await request(app)
        .post('/api/auth/register')
        .send(validUser);

      expect(response.status).toBe(201);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toHaveProperty('token');
      expect(response.body.data).toHaveProperty('user');
      expect(response.body.data.user.username).toBe('testuser');
      expect(response.body.data.user.email).toBe('test@example.com');
      expect(response.body.data.user).not.toHaveProperty('password_hash');
      expect(response.body.data.user).not.toHaveProperty('password');
    });

    it('should hash password before storing', async () => {
      // @ralph 密码安全是否验证？
      await request(app)
        .post('/api/auth/register')
        .send(validUser);

      const user = await prisma.user.findUnique({
        where: { username: 'testuser' }
      });

      expect(user).toBeDefined();
      expect(user!.password_hash).not.toBe('SecurePass123!');
      expect(user!.password_hash).toHaveLength(60); // bcrypt length
    });

    it('should reject duplicate username', async () => {
      // @ralph 唯一性约束是否生效？
      await request(app)
        .post('/api/auth/register')
        .send(validUser);

      const response = await request(app)
        .post('/api/auth/register')
        .send({
          ...validUser,
          email: 'another@example.com'
        });

      expect(response.status).toBe(409);
      expect(response.body.success).toBe(false);
      expect(response.body.error).toContain('用户名');
    });

    it('should reject duplicate email', async () => {
      // @ralph 邮箱唯一性是否检查？
      await request(app)
        .post('/api/auth/register')
        .send(validUser);

      const response = await request(app)
        .post('/api/auth/register')
        .send({
          ...validUser,
          username: 'anotheruser'
        });

      expect(response.status).toBe(409);
      expect(response.body.error).toContain('邮箱');
    });

    it('should reject weak password (too short)', async () => {
      // @ralph 密码强度是否验证？
      const response = await request(app)
        .post('/api/auth/register')
        .send({
          username: 'test',
          email: 'test@test.com',
          password: '12345' // too short
        });

      expect(response.status).toBe(400);
      expect(response.body.success).toBe(false);
      expect(response.body.error).toContain('密码');
    });

    it('should reject missing username', async () => {
      // @ralph 必填字段是否验证？
      const response = await request(app)
        .post('/api/auth/register')
        .send({
          email: 'test@test.com',
          password: 'SecurePass123!'
          // missing username
        });

      expect(response.status).toBe(400);
      expect(response.body.success).toBe(false);
    });

    it('should reject missing password', async () => {
      // @ralph 所有必填字段是否都验证？
      const response = await request(app)
        .post('/api/auth/register')
        .send({
          username: 'test',
          email: 'test@test.com'
          // missing password
        });

      expect(response.status).toBe(400);
    });

    it('should accept registration without email', async () => {
      // @ralph 可选字段是否正确处理？
      const response = await request(app)
        .post('/api/auth/register')
        .send({
          username: 'testuser',
          password: 'SecurePass123!'
          // email is optional
        });

      expect(response.status).toBe(201);
      expect(response.body.data.user.email).toBeNull();
    });
  });

  describe('POST /api/auth/login', () => {
    beforeEach(async () => {
      // Create test user
      const hash = await PasswordService.hash('SecurePass123!');
      await prisma.user.create({
        data: {
          username: 'loginuser',
          email: 'login@test.com',
          password_hash: hash
        }
      });
    });

    it('should login with correct username and password', async () => {
      // @ralph 成功登录流程是否正确？
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          username: 'loginuser',
          password: 'SecurePass123!'
        });

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data).toHaveProperty('token');
      expect(response.body.data.user.username).toBe('loginuser');
    });

    it('should login with correct email and password', async () => {
      // @ralph 邮箱登录是否支持？
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          username: 'login@test.com', // using email as username
          password: 'SecurePass123!'
        });

      expect(response.status).toBe(200);
      expect(response.body.data).toHaveProperty('token');
    });

    it('should reject wrong password', async () => {
      // @ralph 错误密码是否被拒绝？
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          username: 'loginuser',
          password: 'WrongPassword123!'
        });

      expect(response.status).toBe(401);
      expect(response.body.success).toBe(false);
      expect(response.body.error).toContain('密码');
    });

    it('should reject non-existent user', async () => {
      // @ralph 不存在的用户是否被拒绝？
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          username: 'nonexistent',
          password: 'password123'
        });

      expect(response.status).toBe(401);
      expect(response.body.success).toBe(false);
    });

    it('should reject missing username', async () => {
      // @ralph 缺失字段是否处理？
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          password: 'password123'
        });

      expect(response.status).toBe(400);
    });

    it('should reject missing password', async () => {
      // @ralph 缺失密码是否处理？
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          username: 'loginuser'
        });

      expect(response.status).toBe(400);
    });

    it('should reject empty username', async () => {
      // @ralph 空值是否验证？
      const response = await request(app)
        .post('/api/auth/login')
        .send({
          username: '',
          password: 'password123'
        });

      expect(response.status).toBe(400);
    });
  });

  describe('GET /api/auth/me', () => {
    let user: any;
    let token: string;

    beforeEach(async () => {
      // Create test user and generate token
      const hash = await PasswordService.hash('password123');
      user = await prisma.user.create({
        data: {
          username: 'meuser',
          email: 'me@test.com',
          password_hash: hash
        }
      });

      token = AuthService.generateToken({ user_id: user.id });
    });

    it('should return current user with valid token', async () => {
      // @ralph 获取当前用户是否正确？
      const response = await request(app)
        .get('/api/auth/me')
        .set('Authorization', `Bearer ${token}`);

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data.id).toBe(user.id);
      expect(response.body.data.username).toBe('meuser');
      expect(response.body.data.email).toBe('me@test.com');
      expect(response.body.data).not.toHaveProperty('password_hash');
    });

    it('should reject request without token', async () => {
      // @ralph 未认证请求是否被拒绝？
      const response = await request(app)
        .get('/api/auth/me');

      expect(response.status).toBe(401);
      expect(response.body.success).toBe(false);
    });

    it('should reject request with invalid token', async () => {
      // @ralph 无效token是否被拒绝？
      const response = await request(app)
        .get('/api/auth/me')
        .set('Authorization', 'Bearer invalid-token-12345');

      expect(response.status).toBe(401);
    });

    it('should reject request with malformed header', async () => {
      // @ralph 格式错误是否处理？
      const response = await request(app)
        .get('/api/auth/me')
        .set('Authorization', 'InvalidFormat token');

      expect(response.status).toBe(401);
    });

    it('should reject request with empty token', async () => {
      // @ralph 空token是否处理？
      const response = await request(app)
        .get('/api/auth/me')
        .set('Authorization', 'Bearer ');

      expect(response.status).toBe(401);
    });

    it('should reject expired token', async () => {
      // @ralph 过期token是否被拒绝？
      const jwt = require('jsonwebtoken');
      const expiredToken = jwt.sign(
        { user_id: user.id },
        process.env.JWT_SECRET!,
        { expiresIn: '0s' }
      );

      // Wait for token to expire
      await new Promise(resolve => setTimeout(resolve, 100));

      const response = await request(app)
        .get('/api/auth/me')
        .set('Authorization', `Bearer ${expiredToken}`);

      expect(response.status).toBe(401);
    });
  });

  describe('Data Isolation', () => {
    it('should not allow user to access other user data', async () => {
      // @ralph 数据隔离是否正确实现？
      const user1 = await prisma.user.create({
        data: {
          username: 'user1',
          email: 'user1@test.com',
          password_hash: await PasswordService.hash('pass123')
        }
      });

      const user2 = await prisma.user.create({
        data: {
          username: 'user2',
          email: 'user2@test.com',
          password_hash: await PasswordService.hash('pass123')
        }
      });

      // Create document for user1
      await prisma.document.create({
        data: {
          user_id: user1.id,
          content: 'User 1 private document'
        }
      });

      // Try to access with user2 token
      const user2Token = AuthService.generateToken({ user_id: user2.id });

      const response = await request(app)
        .get('/api/documents')
        .set('Authorization', `Bearer ${user2Token}`);

      // Should only return user2's documents (empty in this case)
      expect(response.status).toBe(200);
      expect(response.body.data).toHaveLength(0);
    });
  });

  describe('POST /api/auth/logout', () => {
    it('should return success on logout', async () => {
      // @ralph 登出是否正确处理？
      // Note: With JWT, logout is typically client-side (removing token)
      // Server-side may implement a blacklist if needed

      const response = await request(app)
        .post('/api/auth/logout')
        .send();

      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
    });
  });
});
