<?php

namespace Tests\Support\v2;

use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\FeatureTestTrait;
use Firebase\JWT\JWT;

/**
 * Base test case for v2 Master Data tests
 * 
 * Provides common setup, authentication, and helper methods
 * for all v2 master test feature and unit tests.
 */
abstract class MasterTestCase extends CIUnitTestCase
{
  use FeatureTestTrait;

  /**
   * JWT token for authentication
   */
  protected ?string $token = null;

  /**
   * Test site ID
   */
  protected int $testSiteId = 1;

  /**
   * Test site code
   */
  protected string $testSiteCode = 'TEST01';

  /**
   * Valueset IDs for test types
   */
  public const VALUESET_TEST_TYPE = 27; // VSetID for Test Types
  public const VALUESET_RESULT_TYPE = 43; // VSetID for Result Types
  public const VALUESET_REF_TYPE = 44; // VSetID for Reference Types
  public const VALUESET_ENTITY_TYPE = 39; // VSetID for Entity Types

  /**
   * Test Type VIDs
   */
  public const TEST_TYPE_TEST = 1; // VID for TEST
  public const TEST_TYPE_PARAM = 2; // VID for PARAM
  public const TEST_TYPE_CALC = 3; // VID for CALC
  public const TEST_TYPE_GROUP = 4; // VID for GROUP
  public const TEST_TYPE_TITLE = 5; // VID for TITLE

  /**
   * Setup test environment
   */
  protected function setUp(): void
  {
    parent::setUp();
    $this->token = $this->generateTestToken();
  }

  /**
   * Cleanup after test
   */
  protected function tearDown(): void
  {
    parent::tearDown();
  }

  /**
   * Generate JWT token for testing
   */
  protected function generateTestToken(): string
  {
    $key = getenv('JWT_SECRET') ?: 'my-secret-key';
    $payload = [
      'iss' => 'localhost',
      'aud' => 'localhost',
      'iat' => time(),
      'nbf' => time(),
      'exp' => time() + 3600,
      'uid' => 1,
      'email' => 'admin@admin.com'
    ];
    return JWT::encode($payload, $key, 'HS256');
  }

  /**
   * Make authenticated GET request
   */
  protected function get(string $path, array $options = [])
  {
    $this->withHeaders(['Authorization' => 'Bearer ' . $this->token]);
    return $this->call('get', $path, $options);
  }

  /**
   * Make authenticated POST request
   */
  protected function post(string $path, array $options = [])
  {
    $this->withHeaders(['Authorization' => 'Bearer ' . $this->token]);
    return $this->call('post', $path, $options);
  }

  /**
   * Make authenticated PUT request
   */
  protected function put(string $path, array $options = [])
  {
    $this->withHeaders(['Authorization' => 'Bearer ' . $this->token]);
    return $this->call('put', $path, $options);
  }

  /**
   * Make authenticated DELETE request
   */
  protected function delete(string $path, array $options = [])
  {
    $this->withHeaders(['Authorization' => 'Bearer ' . $this->token]);
    return $this->call('delete', $path, $options);
  }

  /**
   * Create a TEST type test definition
   */
  protected function createTestData(): array
  {
    return [
      'SiteID' => 1,
      'TestSiteCode' => $this->testSiteCode,
      'TestSiteName' => 'Test Definition ' . time(),
      'TestType' => self::TEST_TYPE_TEST,
      'Description' => 'Test description',
      'SeqScr' => 10,
      'SeqRpt' => 10,
      'IndentLeft' => 0,
      'VisibleScr' => 1,
      'VisibleRpt' => 1,
      'CountStat' => 1,
      'details' => [
        'DisciplineID' => 1,
        'DepartmentID' => 1,
        'ResultType' => 1, // Numeric
        'RefType' => 1, // NMRC
        'Unit1' => 'mg/dL',
        'Decimal' => 2,
        'Method' => 'Test Method',
        'ExpectedTAT' => 60
      ],
      'testmap' => [
        [
          'HostType' => 'HIS',
          'HostID' => 'TEST001',
          'HostTestCode' => 'TEST001',
          'HostTestName' => 'Test (HIS)'
        ]
      ]
    ];
  }

  /**
   * Create a PARAM type test definition
   */
  protected function createParamData(): array
  {
    return [
      'SiteID' => 1,
      'TestSiteCode' => 'PARM' . substr(time(), -4),
      'TestSiteName' => 'Parameter Test ' . time(),
      'TestType' => self::TEST_TYPE_PARAM,
      'Description' => 'Parameter test description',
      'SeqScr' => 5,
      'SeqRpt' => 5,
      'VisibleScr' => 1,
      'VisibleRpt' => 1,
      'CountStat' => 1,
      'details' => [
        'DisciplineID' => 1,
        'DepartmentID' => 1,
        'ResultType' => 1,
        'RefType' => 1,
        'Unit1' => 'unit',
        'Decimal' => 1,
        'Method' => 'Parameter Method'
      ]
    ];
  }

  /**
   * Create a GROUP type test definition with members
   */
  protected function createGroupData(array $memberIds = []): array
  {
    return [
      'SiteID' => 1,
      'TestSiteCode' => 'GRUP' . substr(time(), -4),
      'TestSiteName' => 'Group Test ' . time(),
      'TestType' => self::TEST_TYPE_GROUP,
      'Description' => 'Group test description',
      'SeqScr' => 100,
      'SeqRpt' => 100,
      'VisibleScr' => 1,
      'VisibleRpt' => 1,
      'CountStat' => 1,
      'Members' => $memberIds ?: [1, 2],
      'testmap' => [
        [
          'HostType' => 'LIS',
          'HostID' => 'LIS001',
          'HostTestCode' => 'PANEL',
          'HostTestName' => 'Test Panel (LIS)'
        ]
      ]
    ];
  }

  /**
   * Create a CALC type test definition
   */
  protected function createCalcData(): array
  {
    return [
      'SiteID' => 1,
      'TestSiteCode' => 'CALC' . substr(time(), -4),
      'TestSiteName' => 'Calculated Test ' . time(),
      'TestType' => self::TEST_TYPE_CALC,
      'Description' => 'Calculated test description',
      'SeqScr' => 50,
      'SeqRpt' => 50,
      'VisibleScr' => 1,
      'VisibleRpt' => 1,
      'CountStat' => 1,
      'details' => [
        'DisciplineID' => 1,
        'DepartmentID' => 1,
        'FormulaInput' => '["TEST1", "TEST2"]',
        'FormulaCode' => 'TEST1 + TEST2',
        'FormulaLang' => 'SQL',
        'RefType' => 1,
        'Unit1' => 'mg/dL',
        'Decimal' => 0,
        'Method' => 'Calculation Method'
      ],
      'testmap' => [
        [
          'HostType' => 'LIS',
          'HostID' => 'LIS001',
          'HostTestCode' => 'CALCR',
          'HostTestName' => 'Calculated Result (LIS)'
        ]
      ]
    ];
  }

  /**
   * Assert API response has success status
   */
  protected function assertSuccessResponse($response, string $message = 'Response should be successful'): void
  {
    $body = json_decode($response->response()->getBody(), true);
    $this->assertArrayHasKey('status', $body, $message);
    $this->assertEquals('success', $body['status'], $message);
  }

  /**
   * Assert API response has error status
   */
  protected function assertErrorResponse($response, string $message = 'Response should be an error'): void
  {
    $body = json_decode($response->response()->getBody(), true);
    $this->assertArrayHasKey('status', $body, $message);
    $this->assertNotEquals('success', $body['status'], $message);
  }

  /**
   * Assert response has data key
   */
  protected function assertHasData($response, string $message = 'Response should have data'): void
  {
    $body = json_decode($response->response()->getBody(), true);
    $this->assertArrayHasKey('data', $body, $message);
  }

  /**
   * Get test type name from VID
   */
  protected function getTestTypeName(int $vid): string
  {
    return match ($vid) {
      self::TEST_TYPE_TEST => 'TEST',
      self::TEST_TYPE_PARAM => 'PARAM',
      self::TEST_TYPE_CALC => 'CALC',
      self::TEST_TYPE_GROUP => 'GROUP',
      self::TEST_TYPE_TITLE => 'TITLE',
      default => 'UNKNOWN'
    };
  }

  /**
   * Skip test if database not available
   */
  protected function requireDatabase(): void
  {
    $db = \Config\Database::connect();
    try {
      $db->connect();
    } catch (\Exception $e) {
      $this->markTestSkipped('Database not available: ' . $e->getMessage());
    }
  }

  /**
   * Skip test if required seeded data not found
   */
  protected function requireSeededData(): void
  {
    $db = \Config\Database::connect();
    $count = $db->table('valueset')
      ->where('VSetID', self::VALUESET_TEST_TYPE)
      ->countAllResults();

    if ($count === 0) {
      $this->markTestSkipped('Test type valuesets not seeded');
    }
  }
}
