<?php defined('BASEPATH') OR exit('No direct script access allowed');

class Formatter
{
	public $flag = "INVOICE";

    private $ci = null;
	private $cId = 0;
	private $bId = 0;

	public function __construct()
	{
		$this->ci =& get_instance();
		$this->cId = get_company('id');
		$this->bId = get_user_data("defaultbId");
	}

	private function prepare_tax($trans)
	{
		if (empty($trans["tax"]))
		{
			return false;
		}

		$return = array();
		// $totalLevy = array();

		foreach ($trans["tax"] as $tax)
        {
            $itemId = $tax["transItemID"];

            if (empty($return[$itemId])) {
                $return[$itemId] = array(
                    "AssVal"      => $trans['taxableAmount'] ? $trans['taxableAmount'] : 0,
                    "CgstVal"     => 0,
                    "SgstVal"     => 0,
                    "IgstVal"     => 0,
                    "CesVal"      => 0,
                    "StCesVal"    => 0,
                    "Discount"    => 0,
                    "OthChrg"     => 0,
                    "RndOffAmt"   => $trans['roundAmount'] ? $trans['roundAmount'] : 0,
                    "TotInvVal"   => 0,
                    "TotInvValFc" => 0
                );
            }

            switch(trim($tax["taxTypeCode"]))
            {
                case "CGST":
                    $return[$itemId]["CgstVal"] = $tax["taxValue"];
                    break;

                case "SGST":
                    $return[$itemId]["SgstVal"] = $tax["taxValue"];
                    break;

                case "IGST":
                    $return[$itemId]["IgstVal"] = $tax["taxValue"];
                    break;
            }
        }

        foreach ($return as $itemId => $t) {
            $return[$itemId]["TotInvVal"] =
                $t["AssVal"] +
                $t["CgstVal"] +
                $t["SgstVal"] +
                $t["IgstVal"];
        }

		return $return;
	}

	// private function prepare_attributes($detail)
	// {
	// 	$return = array(
	// 		"batch" => "",
	// 		"expiryDate" => "",
	// 		"serialNo" => ""
	// 	);

	// 	$detail;

	// 	return $return;
	// }

    private function assign_datatype($item)
    {
        $fields_map = array(
            'SlNo' => 'S' ,'PrdDesc' => 'S','IsServc' => 'S','Unit' => 'S','HsnCd' =>'S' ,'Qty' => 'I','FreeQty' => 'I',
            'TotAmt' => 'I','UnitPrice' => 'I','TotItemVal' => 'I','Discount' => 'I','AssAmt' => 'I',
            'CgstAmt' => 'I', 'SgstAmt' => 'I', 'IgstAmt' => 'I', 'CesAmt' => 'I', 'GstRt' => 'I', 'CesRt' => 'I'
        );

        $fields = array_keys($fields_map);

        foreach ($item as $field => $value)
        {
            if (@!in_array($field, $fields) || is_null($item[$field]))
            {
                unset($item[$field]);
            } else {
                $item[$field] = $fields_map[$field] == 'I'?(float)$item[$field]:$item[$field];
            }
        }
        $item['SlNo'] 		= (string)$item['SlNo'];

        return $item;
    }

    private function format_address2($addr)
    {
        $a1 = trim(!empty($addr['address1'])?$addr['address1']:'');
        $a2 = trim(!empty($addr['address2'])?$addr['address2']:'');

        if (!$a2 && ($p = strrpos($a1, ',')) !== false) {
            $a2 = trim(substr($a1, $p + 1));
            $a1 = trim(substr($a1, 0, $p));
        }

        if (!$a2) $a2 = $a1;

        $addr['address1'] = $a1;
        $addr['address2'] = $a2;

        return $addr;
    }


	public function format($trans=array())
	{
		$taxes = $this->prepare_tax($trans);

		$return = array(
			"Version" => "1.1",
			"ItemList" => array(),
            "TranDtls" => array(),
            "DocDtls" => array(),
            "SellerDtls" => array(),
            "BuyerDtls" => array(),
            "ValDtls" => array()
		);

        $trans['classCode'] = $trans['classCode']=='DEEM'?'DEXP':$trans['classCode'];

        $exp_sez = false;
		if ($trans['categoryCode']=='EXP' || in_array($trans['classCode'], array("SEZWOP"))) {
			$exp_sez = true;
		}

        $return["TranDtls"] = array(
            "TaxSch"      => "GST",
            "SupTyp" => (in_array($trans['tType'],array(SALINV,CRNOTE)) && $trans['classCode']!='REG')?$trans['classCode']:$trans['categoryCode'],
			"RegRev" => !empty($trans['isReverse'])?'Y':'N', 
            "IgstOnIntra" => "N",
        );

        if (preg_match('/^(0|\/|-)/',$trans['tNo'])) {
				$this->message = lang('EINVOICE_TNO_ERROR');
				return false;
		}

        if (sal_mod_active(BRANCH_MOD)){
				if ($this->bId!=$trans['bId']){
					$this->message = lang('BRANCH_SWAPPED');
					return false;
				}
		}

        $map_conf = array(
            'd_typ' => array(SALINV => 'INV', CRNOTE => 'CRN', DBNOTE => 'DBN',SALRETAIL => 'INV')
        );

       $return["DocDtls"] = array(
            "Typ" => $map_conf['d_typ'][$trans['tType']], 
            "No"  => $trans['tNo']   ? $trans['tNo'] : "", 
            "Dt"  => $trans['tDate'] ? date("d/m/Y", strtotime($trans['tDate'])) : "", 
        );

        $company = $trans["company"];

		$company['Stcd'] = !empty($trans["states"][$company['stateId']]) ? $trans["states"][$company['stateId']]['tinCode'] : 0;
        $company = $this->format_address2($company);

        if (empty($company['address2']) && strrpos($company['address1'], ','))
		{
			$company['address2'] = trim(substr($company['address1'], strrpos($company['address1'], ',')+1));
			$company['address1'] = trim(substr($company['address1'], 0, strrpos($company['address1'], ',')));
		}

		if (empty($company['address2']))
		{
			$company['address2'] = $company['address1'];
		}

        $return["SellerDtls"] = array(
            "Gstin"  => $trans['gstin'],
            "LglNm"  => $company["name"],
            "TrdNm"  => $company["name"],
            "Addr1"  => $company['address1'],
            "Addr2"  => $company['address2'],
            "Loc"    => $company['city'],
            "Pin"    => $company['zip'],
            "Stcd"   => $company['Stcd']
        );

        if (!empty($comp['email'])){
			$return['SellerDtls']['Em'] = $company['email'];
		}
        
        if (!empty($company['phone'])) {
			$return["SellerDtls"]["Ph"] = preg_split('/[\s,;:]+/',preg_replace('/[^\d,;:]/', '', $company['phone']))[0];
		}


        if ($trans['categoryCode']=='EXP' || $trans['classCode']=='EXPWOP')
        {
            //17. In case of transaction of direct export, recipient GSTIN has to be URP and state code has to be 96, PIN code should be 999999, POS should be 96.
            foreach ($trans['address'] as $key => $addr)
            {
                $trans['contactTaxNo'] = $trans['conTaxNo'] = 'URP';
                $addr['Stcd'] = "96";
                $addr['zip'] = "999999";
                $addr['stateId'] = $trans['conStateId'] = -1;
                $states[$trans['conStateId']] = array('tinCode' => "96");
                $trans_addr[$key] = $addr;
            }

            $return['expDtls'] = array(
                "ForCur"	=> $trans['expCurrencyCode'],
                "CntCode" => $addr['countryCode']
            );
        }

       	$address = isset($trans['address'][0]) ? $trans['address'][0] : array(); 
        $address = $this->format_address2($address);
        $return["BuyerDtls"] = array(
            "Gstin" => $trans['contact']['taxNo'],
            "LglNm" => $trans['contact']['company'],
            "TrdNm" => $trans['contact']['company'],
            "Pos"   => $trans['contactTinCode'],
            "Addr1" => $address['address1'],
            "Addr2" => $address['address2'],
            "Loc"   => $address['city'],
            "Pin"   => (int)$address['zip'],
            "Stcd"  => $trans['contactTinCode']
        );

        $ship_addr = isset($trans['address'][1]) ? $trans['address'][1] : array();
        if (!empty($ship_addr))
        {
            $ship_addr = $this->format_address2($ship_addr);

            $return["ShipDtls"] = array(
                "Gstin"	=>  $trans['conTaxNo'],
                "LglNm" => $ship_addr['name'],
                "TrdNm" => $ship_addr['name'],
                "Addr1"	=> $ship_addr['address1'],
                "Addr2"	=> $ship_addr['address2'],
                "Loc"	=> $ship_addr['city'],
                "Pin"	=> (int)$ship_addr['zip'],
                "Stcd"	=> !empty($trans["states"][$ship_addr['stateId']])?$trans["states"][$ship_addr['stateId']]['tinCode']:0
            );
        }

        $serialNo = $discount = $othCharges = 0;
        $excludeUnits = ['LAL', 'AAL', 'LBT'];
        $trans['AssVal'] = 0;
		foreach ($trans["details"] as $detail)
        {
            /* recalculate taxable amount and for discount */
            if (!empty($detail['_discAmount']))
            {
                $detail['taxableAmount'] = (float)$detail['taxableAmount'] - (float)$detail['_discAmount'];
                $detail['itemTotal'] = (float)$detail['itemTotal'] - (float)$detail['_discAmount'];
            }
            $unit = strtoupper(!empty($detail['unit']) ? $detail['unit'] : $detail['baseUnit']);

            // ---------- TAX DETECTION ----------
            $hasTax = (
                !empty($taxes[$detail['id']]) &&
                (
                    ($taxes[$detail['id']]['CgstVal']) > 0 ||
                    ($taxes[$detail['id']]['SgstVal']) > 0 ||
                    ($taxes[$detail['id']]['IgstVal']) > 0
                )
            );

            // ---------- OTHER CHARGES / DISCOUNT ----------
            if (
                $detail['itemType'] === 'A' &&
                !in_array($unit, $excludeUnits, true) &&
                !$hasTax
            ) {
                if ($unit === 'LAT') {
                    // Discount
                    $discount += (float)$detail['itemTotal'];
                } else {
                    // Other Charges
                    $othCharges += (float)$detail['itemTotal'];
                }

                // A-type charges should NOT go to ItemList
                continue;
            }

            // ---------- ITEM LIST (same as earlier SQL) ----------
            if ($detail['itemType'] === 'A' && !$hasTax) {
                // Safety: skip any leftover A-items without tax
                continue;
            }

            $serialNo++;
            $tax = !empty($taxes[$detail["id"]]) ? $taxes[$detail["id"]] : array(
                "AssVal" => "",
                "CgstVal" => "",
                "SgstVal" => "",
                "IgstVal" => "",
                "CesVal" => "",
                "StCesVal" => ""
            );
            $IsServc = ($detail["itemType"] == 'S' || substr(str_replace(' ', '', $detail["itemHS"] ?? ''), 0, 2) == '99') ? 'Y' : 'N';
            $Unit = $IsServc=='Y'?'OTH':strtoupper(!empty($detail["unit"]) ? $detail["unit"] : $detail["baseUnit"]); 
            
            if (!empty($trans['units']) && !empty($trans['units'][$Unit]))
            {
                $Unit = $trans['units'][$Unit]['uqcCode'];
            }

            $itemName = trim($detail['itemName']);
            $itemDesc = !empty($detail['itemDesc'])?trim($detail['itemDesc']):'';
            $maxLen   = 300;

            if ($itemDesc && strlen($itemName) < $maxLen - 3) {
                $itemDesc = substr($itemDesc, 0, $maxLen - strlen($itemName) - 3);
                $detail['itemName'] = $itemDesc !== '' ? $itemName . ' (' . $itemDesc . ')': $itemName;
            } else {
                $detail['itemName'] = substr($itemName, 0, $maxLen);
            }


            $item = array(
                "PrdDesc"    => $detail["itemName"],
                "IsServc"    => $IsServc,
                "Unit"       => $Unit,
                "HsnCd"      => $detail["itemHS"],
                "Qty"        => $detail["qty"],
                "FreeQty"    => $detail["freeQty"],
                "TotAmt"     => $detail["taxableAmount"] ? $detail["taxableAmount"] + (($detail["discAmount"]) ? $detail["discAmount"] : 0) : 0,
                "UnitPrice"  => $detail["rate"], 
                "TotItemVal" => $detail["itemTotal"],
                "Discount"   => $detail["discAmount"],
                "AssAmt"     => $detail["taxableAmount"],
                "CgstAmt"    => $tax["CgstVal"],
                "SgstAmt"    => $tax["SgstVal"],
                "IgstAmt"    => $tax["IgstVal"],
                "CesAmt"     => $tax["CesVal"],
                "GstRt"      => $detail["taxName"] ? preg_replace('/[^0-9]/', '', $detail["taxName"]) : 0,
                "CesRt"      => 0,
                "SlNo"       => $serialNo
            );
            $trans['AssVal']+=$detail["taxableAmount"];
            $return["ItemList"][] = $this->assign_datatype($item);;
        }

        $cgst = 0;
        $sgst = 0;
		$igst = 0;

        if (!empty($trans["tax"])) {
            foreach ($trans["tax"] as $t) {
                if ($t["taxTypeCode"] == "CGST") $cgst += $t["taxValue"];
                if ($t["taxTypeCode"] == "SGST") $sgst += $t["taxValue"];
				if ($t["taxTypeCode"] == "IGST") $igst += $t["taxValue"];
            }
        }

        $trans['totAmount'] = (float)($trans['totAmount'] ?: 0);
        $tax['CgstVal']     = (float)($tax['CgstVal'] ?: 0);
        $tax['SgstVal']      = (float)($tax['SgstVal'] ?: 0);
        $tax['IgstVal']     = (float)($tax['IgstVal'] ?: 0);
        $tax['CesVal']      = (float)($tax['CesVal'] ?: 0);

        $return["ValDtls"] = array(
            "AssVal"     => (float)(!empty($trans['AssVal'])?$trans['AssVal']:$trans['taxableAmount']),
            "CgstVal"    => $cgst,
            "SgstVal"    => $sgst,
            "IgstVal"    => $igst,
            "CesVal"     => 0,
            "StCesVal"   => 0,
            "Discount"   => $discount,
            "OthChrg"    => $othCharges,
            "RndOffAmt"  => (float)$trans['roundAmount'],
            "TotInvVal"  => (float)((!$exp_sez)?$trans['totAmount']:($trans['totAmount']+$tax['CgstVal']+$tax['SgstVal']+$tax['IgstVal']+$tax['CesVal'])),
            "TotInvValFc"=> 0
        );

         // add reedem points to invoice total because points redeem amount reduced from transTotal
         if (sal_mod_active(POINTS_MOD) && !empty($trans['points']) && !empty($trans['points']['pOut_value'])) {
            $return["ValDtls"]['Discount'] += (float)$trans['points']['pOut_value'];
        }

        return $return;
	}
}

