<?php

namespace Tests\Feature\v2\master\TestDef;

use Tests\Support\v2\MasterTestCase;

/**
 * Feature tests for CALC type test definitions
 * 
 * Tests CALC-specific functionality including formula configuration
 */
class TestDefCalcTest extends MasterTestCase
{
  protected string $endpoint = 'v2/master/tests';

  /**
   * Test create CALC with formula
   */
  public function testCreateCalcWithFormula(): void
  {
    $calcData = [
      'SiteID' => 1,
      'TestSiteCode' => 'CALC' . substr(time(), -4),
      'TestSiteName' => 'Calculated Test ' . time(),
      'TestType' => $this::TEST_TYPE_CALC,
      'Description' => 'Calculated test with formula',
      'SeqScr' => 50,
      'SeqRpt' => 50,
      'VisibleScr' => 1,
      'VisibleRpt' => 1,
      'CountStat' => 1,
      'details' => [
        'DisciplineID' => 1,
        'DepartmentID' => 1,
        'FormulaInput' => '["CHOL", "HDL", "TG"]',
        'FormulaCode' => 'CHOL - HDL - (TG / 5)',
        'FormulaLang' => 'SQL',
        'RefType' => 1, // NMRC
        'Unit1' => 'mg/dL',
        'Decimal' => 0,
        'Method' => 'Friedewald Formula'
      ]
    ];

    $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"
    );

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

      // Verify calc details were created
      $calcId = $body['data']['TestSiteId'];
      $showResult = $this->get($this->endpoint . '/' . $calcId);
      $showBody = json_decode($showResult->response()->getBody(), true);

      if ($showBody['data'] !== null) {
        $this->assertArrayHasKey('testdefcal', $showBody['data']);
      }
    }
  }

  /**
   * Test CALC with different formula languages
   */
  public function testCalcWithDifferentFormulaLanguages(): void
  {
    $languages = ['Phyton', 'CQL', 'FHIRP', 'SQL'];

    foreach ($languages as $lang) {
      $calcData = [
        'SiteID' => 1,
        'TestSiteCode' => 'C' . substr(time(), -5) . strtoupper(substr($lang, 0, 1)),
        'TestSiteName' => "Calc with $lang",
        'TestType' => $this::TEST_TYPE_CALC,
        'details' => [
          'FormulaInput' => '["TEST1"]',
          'FormulaCode' => 'TEST1 * 2',
          'FormulaLang' => $lang
        ]
      ];

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

      $this->assertTrue(
        in_array($status, [201, 400, 500]),
        "CALC with $lang: Expected 201, 400, or 500, got $status"
      );
    }
  }

  /**
   * Test CALC with JSON formula input
   */
  public function testCalcWithJsonFormulaInput(): void
  {
    $calcData = [
      'SiteID' => 1,
      'TestSiteCode' => 'CJSN' . substr(time(), -3),
      'TestSiteName' => 'Calc with JSON Input',
      'TestType' => $this::TEST_TYPE_CALC,
      'details' => [
        'FormulaInput' => '["parameter1", "parameter2", "parameter3"]',
        'FormulaCode' => '(param1 + param2) / param3',
        'FormulaLang' => 'FHIRP'
      ]
    ];

    $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 CALC with complex formula
   */
  public function testCalcWithComplexFormula(): void
  {
    $calcData = [
      'SiteID' => 1,
      'TestSiteCode' => 'CCMP' . substr(time(), -3),
      'TestSiteName' => 'Calc with Complex Formula',
      'TestType' => $this::TEST_TYPE_CALC,
      'details' => [
        'FormulaInput' => '["WBC", "NEUT", "LYMPH", "MONO", "EOS", "BASO"]',
        'FormulaCode' => 'if WBC > 0 then (NEUT + LYMPH + MONO + EOS + BASO) / WBC * 100 else 0',
        'FormulaLang' => 'Phyton'
      ]
    ];

    $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 CALC formula
   */
  public function testUpdateCalcFormula(): void
  {
    // Create a CALC first
    $calcData = [
      'SiteID' => 1,
      'TestSiteCode' => 'UPCL' . substr(time(), -4),
      'TestSiteName' => 'Update Calc Test',
      'TestType' => $this::TEST_TYPE_CALC,
      'details' => [
        'FormulaInput' => '["A", "B"]',
        'FormulaCode' => 'A + B',
        'FormulaLang' => 'SQL'
      ]
    ];

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

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

      if ($calcId) {
        // Update formula
        $updateData = [
          'TestSiteName' => 'Updated Calc Test Name',
          'details' => [
            'FormulaInput' => '["A", "B", "C"]',
            'FormulaCode' => 'A + B + C'
          ]
        ];

        $updateResult = $this->put($this->endpoint . '/' . $calcId, ['body' => json_encode($updateData)]);
        $updateStatus = $updateResult->response()->getStatusCode();

        $this->assertTrue(
          in_array($updateStatus, [200, 400, 500]),
          "Expected 200, 400, or 500, got $updateStatus"
        );
      }
    }
  }

  /**
   * Test CALC has correct TypeCode in response
   */
  public function testCalcTypeCodeInResponse(): void
  {
    $indexResult = $this->get($this->endpoint . '?TestType=CALC');
    $indexBody = json_decode($indexResult->response()->getBody(), true);

    if (isset($indexBody['data']) && is_array($indexBody['data']) && !empty($indexBody['data'])) {
      $calc = $indexBody['data'][0];

      // Verify TypeCode is CALC
      $this->assertEquals('CALC', $calc['TypeCode'] ?? '');
    }
  }

  /**
   * Test CALC details structure
   */
  public function testCalcDetailsStructure(): void
  {
    $indexResult = $this->get($this->endpoint . '?TestType=CALC');
    $indexBody = json_decode($indexResult->response()->getBody(), true);

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

      if ($calcId) {
        $showResult = $this->get($this->endpoint . '/' . $calcId);
        $showBody = json_decode($showResult->response()->getBody(), true);

        if ($showBody['data'] !== null && isset($showBody['data']['testdefcal'])) {
          $calcDetails = $showBody['data']['testdefcal'];

          if (is_array($calcDetails) && !empty($calcDetails)) {
            $firstDetail = $calcDetails[0];

            // Check required fields in calc structure
            $this->assertArrayHasKey('TestCalID', $firstDetail);
            $this->assertArrayHasKey('TestSiteID', $firstDetail);
            $this->assertArrayHasKey('FormulaInput', $firstDetail);
            $this->assertArrayHasKey('FormulaCode', $firstDetail);

            // Check for joined discipline/department
            if (isset($firstDetail['DisciplineName'])) {
              $this->assertArrayHasKey('DepartmentName', $firstDetail);
            }
          }
        }
      }
    }
  }

  /**
   * Test CALC delete cascades to details
   */
  public function testCalcDeleteCascadesToDetails(): void
  {
    // Create a CALC
    $calcData = [
      'SiteID' => 1,
      'TestSiteCode' => 'CDEL' . substr(time(), -4),
      'TestSiteName' => 'Calc to Delete',
      'TestType' => $this::TEST_TYPE_CALC,
      'details' => [
        'FormulaInput' => '["TEST1"]',
        'FormulaCode' => 'TEST1 * 2'
      ]
    ];

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

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

      if ($calcId) {
        // Delete the CALC
        $deleteResult = $this->delete($this->endpoint . '/' . $calcId);
        $deleteStatus = $deleteResult->response()->getStatusCode();

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

        if ($deleteStatus === 200) {
          // Verify CALC details are also soft deleted
          $showResult = $this->get($this->endpoint . '/' . $calcId);
          $showBody = json_decode($showResult->response()->getBody(), true);

          // CALC should show EndDate set
          if ($showBody['data'] !== null) {
            $this->assertNotNull($showBody['data']['EndDate']);
          }
        }
      }
    }
  }

  /**
   * Test CALC with result unit configuration
   */
  public function testCalcWithResultUnit(): void
  {
    $units = ['mg/dL', 'g/L', 'mmol/L', '%', 'IU/L'];

    foreach ($units as $unit) {
      $calcData = [
        'SiteID' => 1,
        'TestSiteCode' => 'CUNT' . substr(time(), -3) . substr($unit, 0, 1),
        'TestSiteName' => "Calc with $unit",
        'TestType' => $this::TEST_TYPE_CALC,
        'details' => [
          'Unit1' => $unit,
          'Decimal' => 2,
          'FormulaInput' => '["TEST1"]',
          'FormulaCode' => 'TEST1'
        ]
      ];

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

      $this->assertTrue(
        in_array($status, [201, 400, 500]),
        "CALC with unit $unit: Expected 201, 400, or 500, got $status"
      );
    }
  }
}
