<?php

namespace Tests\Feature\v2\master\TestDef;

use Tests\Support\v2\MasterTestCase;

/**
 * Feature tests for v2 Test Definition API endpoints
 * 
 * Tests CRUD operations for TEST, PARAM, GROUP, and CALC types
 */
class TestDefSiteTest extends MasterTestCase
{
  protected string $endpoint = 'v2/master/tests';

  /**
   * Test index endpoint returns list of tests
   */
  public function testIndexReturnsTestList(): void
  {
    $result = $this->get($this->endpoint);
    $result->assertStatus(200);

    $body = json_decode($result->response()->getBody(), true);
    $this->assertArrayHasKey('status', $body);
    $this->assertArrayHasKey('data', $body);
    $this->assertArrayHasKey('message', $body);
  }

  /**
   * Test index with SiteID filter
   */
  public function testIndexWithSiteFilter(): void
  {
    $result = $this->get($this->endpoint . '?SiteID=1');
    $result->assertStatus(200);

    $body = json_decode($result->response()->getBody(), true);
    $this->assertEquals('success', $body['status']);
  }

  /**
   * Test index with TestType filter
   */
  public function testIndexWithTestTypeFilter(): void
  {
    // Filter by TEST type
    $result = $this->get($this->endpoint . '?TestType=TEST');
    $result->assertStatus(200);

    $body = json_decode($result->response()->getBody(), true);
    $this->assertEquals('success', $body['status']);
  }

  /**
   * Test index with Visibility filter
   */
  public function testIndexWithVisibilityFilter(): void
  {
    $result = $this->get($this->endpoint . '?VisibleScr=1&VisibleRpt=1');
    $result->assertStatus(200);

    $body = json_decode($result->response()->getBody(), true);
    $this->assertEquals('success', $body['status']);
  }

  /**
   * Test index with keyword search
   */
  public function testIndexWithKeywordSearch(): void
  {
    $result = $this->get($this->endpoint . '?TestSiteName=hemoglobin');
    $result->assertStatus(200);

    $body = json_decode($result->response()->getBody(), true);
    $this->assertEquals('success', $body['status']);
  }

  /**
   * Test show endpoint returns single test
   */
  public function testShowReturnsSingleTest(): void
  {
    // First get the list to find a valid ID
    $indexResult = $this->get($this->endpoint);
    $indexBody = json_decode($indexResult->response()->getBody(), true);

    if (isset($indexBody['data']) && is_array($indexBody['data']) && !empty($indexBody['data'])) {
      $firstItem = $indexBody['data'][0];
      $testSiteID = $firstItem['TestSiteID'] ?? null;

      if ($testSiteID) {
        $showResult = $this->get($this->endpoint . '/' . $testSiteID);
        $showResult->assertStatus(200);

        $body = json_decode($showResult->response()->getBody(), true);
        $this->assertArrayHasKey('data', $body);
        $this->assertEquals('success', $body['status']);

        // Check that related details are loaded based on TestType
        if ($body['data'] !== null) {
          $typeCode = $body['data']['TypeCode'] ?? '';
          if ($typeCode === 'CALC') {
            $this->assertArrayHasKey('testdefcal', $body['data']);
          } elseif ($typeCode === 'GROUP') {
            $this->assertArrayHasKey('testdefgrp', $body['data']);
          } elseif (in_array($typeCode, ['TEST', 'PARAM'])) {
            $this->assertArrayHasKey('testdeftech', $body['data']);
          }
          // All types should have testmap
          $this->assertArrayHasKey('testmap', $body['data']);
        }
      }
    }
  }

  /**
   * Test show with non-existent ID returns null data
   */
  public function testShowWithInvalidIDReturnsNull(): void
  {
    $result = $this->get($this->endpoint . '/9999999');
    $result->assertStatus(200);

    $body = json_decode($result->response()->getBody(), true);
    $this->assertArrayHasKey('data', $body);
    $this->assertNull($body['data']);
  }

  /**
   * Test create new TEST type test definition
   */
  public function testCreateTestTypeTest(): void
  {
    $testData = $this->createTestData();

    $result = $this->post($this->endpoint, ['body' => json_encode($testData)]);

    $status = $result->response()->getStatusCode();
    // Expect 201 (created) or 400 (validation error) or 500 (server error)
    $this->assertTrue(
      in_array($status, [201, 400, 500]),
      "Expected 201, 400, or 500, got $status"
    );

    if ($status === 201) {
      $body = json_decode($result->response()->getBody(), true);
      $this->assertEquals('created', $body['status']);
      $this->assertArrayHasKey('TestSiteId', $body['data']);
    }
  }

  /**
   * Test create new PARAM type test definition
   */
  public function testCreateParamTypeTest(): void
  {
    $paramData = $this->createParamData();

    $result = $this->post($this->endpoint, ['body' => json_encode($paramData)]);

    $status = $result->response()->getStatusCode();
    $this->assertTrue(
      in_array($status, [201, 400, 500]),
      "Expected 201, 400, or 500, got $status"
    );
  }

  /**
   * Test create new GROUP type test definition
   */
  public function testCreateGroupTypeTest(): void
  {
    // First create some member tests
    $memberIds = $this->getExistingTestIds();

    $groupData = $this->createGroupData($memberIds);

    $result = $this->post($this->endpoint, ['body' => json_encode($groupData)]);

    $status = $result->response()->getStatusCode();
    $this->assertTrue(
      in_array($status, [201, 400, 500]),
      "Expected 201, 400, or 500, got $status"
    );
  }

  /**
   * Test create new CALC type test definition
   */
  public function testCreateCalcTypeTest(): void
  {
    $calcData = $this->createCalcData();

    $result = $this->post($this->endpoint, ['body' => json_encode($calcData)]);

    $status = $result->response()->getStatusCode();
    $this->assertTrue(
      in_array($status, [201, 400, 500]),
      "Expected 201, 400, or 500, got $status"
    );
  }

  /**
   * Test update existing test
   */
  public function testUpdateTest(): void
  {
    $indexResult = $this->get($this->endpoint);
    $indexBody = json_decode($indexResult->response()->getBody(), true);

    if (isset($indexBody['data']) && is_array($indexBody['data']) && !empty($indexBody['data'])) {
      $firstItem = $indexBody['data'][0];
      $testSiteID = $firstItem['TestSiteID'] ?? null;

      if ($testSiteID) {
        $updateData = [
          'TestSiteName' => 'Updated Test Name ' . time(),
          'Description' => 'Updated description'
        ];

        $result = $this->put($this->endpoint . '/' . $testSiteID, ['body' => json_encode($updateData)]);
        $status = $result->response()->getStatusCode();
        $this->assertTrue(
          in_array($status, [200, 404, 500]),
          "Expected 200, 404, or 500, got $status"
        );

        if ($status === 200) {
          $body = json_decode($result->response()->getBody(), true);
          $this->assertEquals('success', $body['status']);
        }
      }
    }
  }

  /**
   * Test soft delete (disable) test
   */
  public function testDeleteTest(): void
  {
    // Create a test first to delete
    $testData = $this->createTestData();
    $testData['TestSiteCode'] = 'DEL' . substr(time(), -4);

    $createResult = $this->post($this->endpoint, ['body' => json_encode($testData)]);
    $createStatus = $createResult->response()->getStatusCode();

    if ($createStatus === 201) {
      $createBody = json_decode($createResult->response()->getBody(), true);
      $testSiteID = $createBody['data']['TestSiteId'] ?? null;

      if ($testSiteID) {
        $deleteResult = $this->delete($this->endpoint . '/' . $testSiteID);
        $deleteStatus = $deleteResult->response()->getStatusCode();

        $this->assertTrue(
          in_array($deleteStatus, [200, 404, 500]),
          "Expected 200, 404, or 500, got $deleteStatus"
        );

        if ($deleteStatus === 200) {
          $deleteBody = json_decode($deleteResult->response()->getBody(), true);
          $this->assertEquals('success', $deleteBody['status']);
          $this->assertArrayHasKey('EndDate', $deleteBody['data']);
        }
      }
    }
  }

  /**
   * Test validation - missing required fields
   */
  public function testCreateValidationRequiredFields(): void
  {
    $invalidData = [
      'TestSiteName' => 'Test without required fields'
    ];

    $result = $this->post($this->endpoint, ['body' => json_encode($invalidData)]);
    $result->assertStatus(400);
  }

  /**
   * Test that TestSiteCode is max 6 characters
   */
  public function testTestSiteCodeLength(): void
  {
    $invalidData = [
      'SiteID' => 1,
      'TestSiteCode' => 'HB123456', // 8 characters - invalid
      'TestSiteName' => 'Test with too long code',
      'TestType' => $this::TEST_TYPE_TEST
    ];

    $result = $this->post($this->endpoint, ['body' => json_encode($invalidData)]);
    $result->assertStatus(400);
  }

  /**
   * Test that TestSiteCode is at least 3 characters
   */
  public function testTestSiteCodeMinLength(): void
  {
    $invalidData = [
      'SiteID' => 1,
      'TestSiteCode' => 'HB', // 2 characters - invalid
      'TestSiteName' => 'Test with too short code',
      'TestType' => $this::TEST_TYPE_TEST
    ];

    $result = $this->post($this->endpoint, ['body' => json_encode($invalidData)]);
    $result->assertStatus(400);
  }

  /**
   * Test that duplicate TestSiteCode is rejected
   */
  public function testDuplicateTestSiteCode(): void
  {
    // First create a test
    $testData = $this->createTestData();
    $testData['TestSiteCode'] = 'DUP' . substr(time(), -3);

    $this->post($this->endpoint, ['body' => json_encode($testData)]);

    // Try to create another test with the same code
    $duplicateData = $testData;
    $duplicateData['TestSiteName'] = 'Different Name';

    $result = $this->post($this->endpoint, ['body' => json_encode($duplicateData)]);

    // Should fail with 400 or 500
    $status = $result->response()->getStatusCode();
    $this->assertTrue(
      in_array($status, [400, 500]),
      "Expected 400 or 500 for duplicate, got $status"
    );
  }

  /**
   * Test filtering by multiple parameters
   */
  public function testIndexWithMultipleFilters(): void
  {
    $result = $this->get($this->endpoint . '?SiteID=1&TestType=TEST&VisibleScr=1');
    $result->assertStatus(200);

    $body = json_decode($result->response()->getBody(), true);
    $this->assertEquals('success', $body['status']);
  }

  /**
   * Helper method to get existing test IDs for group members
   */
  private function getExistingTestIds(): array
  {
    $indexResult = $this->get($this->endpoint);
    $indexBody = json_decode($indexResult->response()->getBody(), true);

    $ids = [];
    if (isset($indexBody['data']) && is_array($indexBody['data'])) {
      foreach ($indexBody['data'] as $item) {
        if (isset($item['TestSiteID'])) {
          $ids[] = $item['TestSiteID'];
        }
        if (count($ids) >= 2) {
          break;
        }
      }
    }

    return $ids ?: [1, 2];
  }
}
