// src/config/swagger.test.ts
import { describe, it, expect } from 'vitest';
import { swaggerSpec } from './swagger';

// Type definition for OpenAPI 3.0 spec structure used in tests
interface OpenAPISpec {
  openapi: string;
  info: {
    title: string;
    version: string;
    description?: string;
    contact?: { name: string };
    license?: { name: string };
  };
  servers: Array<{ url: string; description?: string }>;
  components: {
    securitySchemes?: {
      bearerAuth?: {
        type: string;
        scheme: string;
        bearerFormat?: string;
        description?: string;
      };
    };
    schemas?: Record<string, unknown>;
  };
  tags: Array<{ name: string; description?: string }>;
  paths?: Record<string, unknown>;
}

// Cast to typed spec for property access
const spec = swaggerSpec as OpenAPISpec;

/**
 * Tests for src/config/swagger.ts - OpenAPI/Swagger configuration.
 *
 * These tests verify the swagger specification structure and content
 * without testing the swagger-jsdoc library itself.
 */
describe('swagger configuration', () => {
  describe('swaggerSpec export', () => {
    it('should export a swagger specification object', () => {
      expect(swaggerSpec).toBeDefined();
      expect(typeof swaggerSpec).toBe('object');
    });

    it('should have openapi version 3.0.0', () => {
      expect(spec.openapi).toBe('3.0.0');
    });
  });

  describe('info section', () => {
    it('should have info object with required fields', () => {
      expect(spec.info).toBeDefined();
      expect(spec.info.title).toBe('Flyer Crawler API');
      expect(spec.info.version).toBe('1.0.0');
    });

    it('should have description', () => {
      expect(spec.info.description).toBeDefined();
      expect(spec.info.description).toContain('Flyer Crawler');
    });

    it('should have contact information', () => {
      expect(spec.info.contact).toBeDefined();
      expect(spec.info.contact?.name).toBe('API Support');
    });

    it('should have license information', () => {
      expect(spec.info.license).toBeDefined();
      expect(spec.info.license?.name).toBe('Private');
    });
  });

  describe('servers section', () => {
    it('should have servers array', () => {
      expect(spec.servers).toBeDefined();
      expect(Array.isArray(spec.servers)).toBe(true);
      expect(spec.servers.length).toBeGreaterThan(0);
    });

    it('should have /api as the server URL', () => {
      const apiServer = spec.servers.find((s) => s.url === '/api');
      expect(apiServer).toBeDefined();
      expect(apiServer?.description).toBe('API server');
    });
  });

  describe('components section', () => {
    it('should have components object', () => {
      expect(spec.components).toBeDefined();
    });

    describe('securitySchemes', () => {
      it('should have bearerAuth security scheme', () => {
        expect(spec.components.securitySchemes).toBeDefined();
        expect(spec.components.securitySchemes?.bearerAuth).toBeDefined();
      });

      it('should configure bearerAuth as HTTP bearer with JWT format', () => {
        const bearerAuth = spec.components.securitySchemes?.bearerAuth;
        expect(bearerAuth?.type).toBe('http');
        expect(bearerAuth?.scheme).toBe('bearer');
        expect(bearerAuth?.bearerFormat).toBe('JWT');
      });

      it('should have description for bearerAuth', () => {
        const bearerAuth = spec.components.securitySchemes?.bearerAuth;
        expect(bearerAuth?.description).toContain('JWT token');
      });
    });

    describe('schemas', () => {
      const schemas = () => spec.components.schemas as Record<string, any>;

      it('should have schemas object', () => {
        expect(spec.components.schemas).toBeDefined();
      });

      it('should have SuccessResponse schema (ADR-028)', () => {
        const schema = schemas().SuccessResponse;
        expect(schema).toBeDefined();
        expect(schema.type).toBe('object');
        expect(schema.properties.success).toBeDefined();
        expect(schema.properties.data).toBeDefined();
        expect(schema.required).toContain('success');
        expect(schema.required).toContain('data');
      });

      it('should have ErrorResponse schema (ADR-028)', () => {
        const schema = schemas().ErrorResponse;
        expect(schema).toBeDefined();
        expect(schema.type).toBe('object');
        expect(schema.properties.success).toBeDefined();
        expect(schema.properties.error).toBeDefined();
        expect(schema.required).toContain('success');
        expect(schema.required).toContain('error');
      });

      it('should have ErrorResponse error object with code and message', () => {
        const errorSchema = schemas().ErrorResponse.properties.error;
        expect(errorSchema.properties.code).toBeDefined();
        expect(errorSchema.properties.message).toBeDefined();
        expect(errorSchema.required).toContain('code');
        expect(errorSchema.required).toContain('message');
      });

      it('should have ServiceHealth schema', () => {
        const schema = schemas().ServiceHealth;
        expect(schema).toBeDefined();
        expect(schema.type).toBe('object');
        expect(schema.properties.status).toBeDefined();
        expect(schema.properties.status.enum).toContain('healthy');
        expect(schema.properties.status.enum).toContain('degraded');
        expect(schema.properties.status.enum).toContain('unhealthy');
      });

      it('should have Achievement schema', () => {
        const schema = schemas().Achievement;
        expect(schema).toBeDefined();
        expect(schema.type).toBe('object');
        expect(schema.properties.achievement_id).toBeDefined();
        expect(schema.properties.name).toBeDefined();
        expect(schema.properties.description).toBeDefined();
        expect(schema.properties.icon).toBeDefined();
        expect(schema.properties.points_value).toBeDefined();
      });

      it('should have UserAchievement schema extending Achievement', () => {
        const schema = schemas().UserAchievement;
        expect(schema).toBeDefined();
        expect(schema.allOf).toBeDefined();
        expect(schema.allOf[0].$ref).toBe('#/components/schemas/Achievement');
      });

      it('should have LeaderboardUser schema', () => {
        const schema = schemas().LeaderboardUser;
        expect(schema).toBeDefined();
        expect(schema.type).toBe('object');
        expect(schema.properties.user_id).toBeDefined();
        expect(schema.properties.full_name).toBeDefined();
        expect(schema.properties.points).toBeDefined();
        expect(schema.properties.rank).toBeDefined();
      });
    });
  });

  describe('tags section', () => {
    it('should have tags array', () => {
      expect(spec.tags).toBeDefined();
      expect(Array.isArray(spec.tags)).toBe(true);
    });

    it('should have Health tag', () => {
      const tag = spec.tags.find((t) => t.name === 'Health');
      expect(tag).toBeDefined();
      expect(tag?.description).toContain('health');
    });

    it('should have Auth tag', () => {
      const tag = spec.tags.find((t) => t.name === 'Auth');
      expect(tag).toBeDefined();
      expect(tag?.description).toContain('Authentication');
    });

    it('should have Users tag', () => {
      const tag = spec.tags.find((t) => t.name === 'Users');
      expect(tag).toBeDefined();
      expect(tag?.description).toContain('User');
    });

    it('should have Achievements tag', () => {
      const tag = spec.tags.find((t) => t.name === 'Achievements');
      expect(tag).toBeDefined();
      expect(tag?.description).toContain('Gamification');
    });

    it('should have Flyers tag', () => {
      const tag = spec.tags.find((t) => t.name === 'Flyers');
      expect(tag).toBeDefined();
    });

    it('should have Recipes tag', () => {
      const tag = spec.tags.find((t) => t.name === 'Recipes');
      expect(tag).toBeDefined();
    });

    it('should have Budgets tag', () => {
      const tag = spec.tags.find((t) => t.name === 'Budgets');
      expect(tag).toBeDefined();
    });

    it('should have Admin tag', () => {
      const tag = spec.tags.find((t) => t.name === 'Admin');
      expect(tag).toBeDefined();
      expect(tag?.description).toContain('admin');
    });

    it('should have System tag', () => {
      const tag = spec.tags.find((t) => t.name === 'System');
      expect(tag).toBeDefined();
    });

    it('should have 9 tags total', () => {
      expect(spec.tags.length).toBe(9);
    });
  });

  describe('specification validity', () => {
    it('should have paths object (may be empty if no JSDoc annotations parsed)', () => {
      // swagger-jsdoc creates paths from JSDoc annotations in route files
      // In test environment, this may be empty if routes aren't scanned
      expect(swaggerSpec).toHaveProperty('paths');
    });

    it('should be a valid JSON-serializable object', () => {
      expect(() => JSON.stringify(swaggerSpec)).not.toThrow();
    });

    it('should produce valid JSON output', () => {
      const json = JSON.stringify(swaggerSpec);
      expect(() => JSON.parse(json)).not.toThrow();
    });
  });
});
