<?php

namespace Accounting\billing;

use Accounting\entries\JournalEntries;
use Accounting\ParamSetup;
use Barryvdh\DomPDF\Facade\Pdf;
use Base\DBQueries;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class Billings extends ParamSetup
{
    private string $tbl = 'tbl_billing_list';
    private Request $request;

    private string $billingNumber;

    private array $details;

    public function setParams(): array { return ['request' => $this->request, 'table' => $this->tbl]; }

    public function main (Request $request) : JsonResponse {
        $this->request = $request;
        if($request->type === 'code') {
            $this->generateTransactionCode();
            return successResponse("msg",$this->billingNumber);
        }
        try {
            if($this->generateParams()){
                $this->filterTable = $this->table;
                $action = $this->action;
                if($this->action === 'dbInsert') {
                    $this->addCustomerCode();
                    $values = $this->values;
                    $dueDate = Carbon::parse($values['billing_date'])->addDays($values['terms'])->format('Y-m-d');
                    $details = $this->getValue('details');
                    $amountDue = (float)(str_replace(',','',$details['amountDue']));
                    $this->details = $details;
                    $this->removeValueArray('details');
                    $this->addValues('amount', $amountDue);
                    $this->addValues('due_date', $dueDate);
                    $this->addValues('outstanding', $amountDue);
                    $this->addValues('payment', 0);
                    $this->addValues('status', 1);
                    $this->billingNumber = $this->getValue('billing_no');
                   // return successResponse("",$this->values);
                    $this->saveBillingDetails();
                    //return successResponse("",$this->values);
                    $this->createReceiptVoucherEntries();

                }
                else if($this->action === 'dbGet') {
                    $conditions = $this->conditions;
                    if(isset($conditions['unit'])){
                        if($conditions['unit'] === 'print'){
                            $this->getBillingForView();
                            return successResponse("",$this->response);
                        }
                    }


                }
                $this->execQuery();
                return $this->getResponse($action);
            }
        }
        catch (\Exception $ex){
            return failedResponse("ERROR", ["msg" => $ex->getMessage()], 422);
        }
        return failedResponse("ERROR", ["msg" => "Unknown Error!"], 400);
    }

    private function getResponse($action) : ?JsonResponse{
        $this->filterResponse();
        $responseMsg = "No Data Found!";
        if($this->response) {
            if ($action === 'dbGet') {
                $response = $this->response;
                $this->responseParser($response);
                $trans = (new DBQueries("tbl_operation_trans", null, conditions: ["trans_no" => $response->trans_code]))->dbGet();
                if($trans) $response->summary = $trans->particulars;
                else $response->summary = null;

                $details = (new DBQueries("tbl_operation_trans_details", null, conditions: ["trans_no" => $response->trans_code]))->dbGetAll();
                $details->map(function ($details){
                    $details->amount = number_format($details->amount, 2, '.',',');
                });
                $response->details = $details;
                $billingDetails = DB::table('tbl_billing_details')->where('billing_number',$response->billing_no)->get();
                $billingDetail = array();
                foreach($billingDetails as $billing){
                    if($billing->type !== 'amount') $billingDetail[$billing->type] = $billing->value;
                }
                $response->billingDetails = $billingDetail;

                $response->client = (new DBQueries("tbl_entities", null, conditions: ["code" => $response->customer_code]))->dbGet();

                return successResponse("Success",$response);
            }
            else if ($action === 'dbGetAll') {
                $response = $this->response;
                foreach($response as $res){
                    $this->responseParser($res);
                }
                return successResponse("Success",$response);
            }
            else $responseMsg = "Success";
        }
        return successResponse($responseMsg, $this->response);
    }

    private function responseParser($response) {
        $outstanding = $response->outstanding;
        $response->dueDate = Carbon::parse($response->billing_date)->addDays($response->terms)->format('d-M-Y');
        $response->billingDate = Carbon::parse($response->billing_date)->format('d-M-Y');
        $response->billingDateDetails = Carbon::parse($response->billing_date)->format('j F Y');
        $response->dueDateDetails = Carbon::parse($response->due_date)->format('j F Y');
        $response->termsDetails = $response->terms.' '.$response->unit;
        $response->amountDetails = number_format($response->amount, 2, '.',',');
        $response->payment = number_format($response->payment, 2, '.',',');
        $response->outstanding = number_format($response->outstanding, 2, '.',',');

        $response->dueDate = Carbon::parse($response->due_date)->format('d-M-Y');
        $overDueDays = Carbon::parse($response->dueDate)->diff(Carbon::now());
        $response->overDueDate = '';
        $response->overDueRange = '';
        $response->isOverDue = false;
        if($outstanding > 0){
            if($overDueDays->invert === 0 && $overDueDays->days > 0){
                $response->overDueDate = '<b style="color:red">'.$response->dueDate.'</b>';
                if($overDueDays->days <= 30) $response->overDueRange = '1-30';
                else if($overDueDays->days <= 60) $response->overDueRange = '31-60';
                else if($overDueDays->days <= 90) $response->overDueRange = '61-90';
                else if($overDueDays->days <= 120) $response->overDueRange = '91-120';
                else $response->overDueRange = 'over 120';
                $response->overDueRange = '<b style="color:red">'.$response->overDueRange.'</b>';
                $response->isOverDue = true;
            }
        }
        else $response->overDueRange = 'Paid';
        return $response;
    }

    private function addCustomerCode() : void {
        $values = $this->values;
        $customer = DB::table('tbl_operation_trans')->select('customer_code')->where('trans_no', $values['trans_code'])->first();
        if($customer){
            $values['customer_code'] = $customer->customer_code;
            $this->values = $values;
        }
    }

    private function generateTransactionCodes() : void {
        $code = date('Ym').'00001';
        $dbCode = DB::table('tbl_billing_list')->orderByDesc('billing_no')->first();
        if($dbCode){
            $code = $dbCode->billing_no;
            $curMonth = date('Ym');
            $month = substr($code,2, 6);
            $incr = substr($code,8);
            if($curMonth === $month){
                $incr = (int)$incr + 1;
                if($incr < 10) $code = $curMonth.'0000'.$incr;
                else if($incr < 100) $code = $curMonth.'000'.$incr;
                else if($incr < 1000) $code = $curMonth.'00'.$incr;
                else if($incr < 10000) $code = $curMonth.'0'.$incr;
                else $code = $curMonth.$incr;
            }
            else $code = $curMonth.'00001';
        }
        $this->billingNumber = 'BN'.$code;
    }

    private function createReceiptVoucherEntries() : void {
        $values = $this->values;
        $entries = array();
        $account = DB::table('tbl_account_mapping')->where('type','billing')->where('category','transactions')
            ->where('map_code','IN')->first();
        if($account){
            $entry['acc_code'] = $account->account_code;
            $entry['trans_code'] = $values['trans_code'];
            $entry['original_cur'] = 'PHP';
            $entry['local_cur'] = 'PHP';
            $entry['oc_value'] = 1;
            $entry['forex_rate'] = 1;
            $entry['lc_value'] = 1;
            $entry['actual_value'] = (float)(str_replace(',','',$values['amount']));
            $entry['normal_balance'] = $account->normal_balance;
            $entry['posted'] = 0;
            $entry['status'] = 1;
            $entries[] = $entry;
        }
        $account2 = DB::table('tbl_account_mapping')->where('map_code',$values['customer_code'])->first();
        if($account2){
            $entry['acc_code'] = $account2->account_code;
            $entry['trans_code'] = $values['trans_code'];
            $entry['original_cur'] = 'PHP';
            $entry['local_cur'] = 'PHP';
            $entry['oc_value'] = 1;
            $entry['forex_rate'] = 1;
            $entry['lc_value'] = 1;
            $entry['actual_value'] = (float)(str_replace(',','',$values['amount']));
            $entry['normal_balance'] = $account2->normal_balance;
            $entry['posted'] = 0;
            $entry['status'] = 1;
            $entries[] = $entry;
        }
       (new JournalEntries)->addEntries($entries);

    }

    private function saveBillingDetails() : void {
        $details = $this->details;
        foreach($details as $key => $detail){
            $values['billing_number'] = $this->billingNumber;
            $values['type'] = $key;
            $values['value'] = $detail;
            DB::table('tbl_billing_details')->insert($values);
        }
    }

    private function getBillingForView() {
        $conditions = $this->conditions;
        unset($conditions['unit']);
        $this->conditions = $conditions;
        $this->execQuery();
        $response = (array) $this->response;
        $transDetails = DB::table('tbl_operation_trans_details')->where('trans_no',$response['trans_code'])->get();

        $details = array();
        $billingDetails = DB::table('tbl_billing_details')->where('billing_number',$response['billing_no'])->get();
        foreach($billingDetails as $billingDetail){
            $details[$billingDetail->type] = $billingDetail->value;
        }
        $response['transDetails'] = $transDetails;
        $response['billingDetails'] = $details;
        $this->response = $response;

    }

    private function billingForView($code) : array {
        $response = DB::table('tbl_billing_list')->where('billing_no',$code)->first();

        if($response){
            $response = (array) $response;
            $transDetails = DB::table('tbl_operation_trans_details')->where('trans_no',$response['trans_code'])->get()->toArray();
            $details = array();
            $billingDetails = DB::table('tbl_billing_details')->where('billing_number',$response['billing_no'])->get();
            foreach($billingDetails as $billingDetail){
                $details[$billingDetail->type] = $billingDetail->value;
            }
            $client = DB::table('tbl_entities')->where('code',$response['customer_code'])->first();
            $clientTin = DB::table('tbl_entity_details')->where('entity_code',$response['customer_code'])->where('type','tinNumber')->first();
            $clientBusType = DB::table('tbl_entity_details')->where('entity_code',$response['customer_code'])->where('type','businessType')->first();
            $response['clientName'] = $client ? $client->name : '';
            $response['clientTIN'] = $clientTin ? $clientTin->value : '';
            $response['clientBusType'] = $clientBusType ? $clientBusType->value : '';
            $response['transDetails'] = $transDetails;
            $response['billingDetails'] = $details;
            return $response;
        }
        return [];


    }

    public function previewBilling($code){
        $data = $this->billingForView($code);
        //return dd($data);
        $pdf_parse = PDF::loadView('templates/reports/billingStatementData',$data);
        $pdf_parse->setPaper('a4', 'portrait');
        return $pdf_parse->stream();
    }

    public function previewAccountsReceivable(){
        $responses = DB::table('tbl_billing_list')->orderBy('id')->get();
        $responses->map(function ($responses){
            $this->responseParser($responses);
        });
        //return dd((array)$responses);
        $data['receivables'] = $responses;
        $pdf_parse = PDF::loadView('templates/reports/accountsReceivableData',$data);
        $pdf_parse->setPaper('a4', 'landscape');
        return $pdf_parse->stream();
    }
}
