<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Routing\ResponseFactory;
use Illuminate\Support\MessageBag;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use App\Booking;
use App\Invoice;
use App\InvoiceDetails;
use App\InvoiceStatuses;
use App\SupplierBooking;
use App\SettingSupplier;
use App\Marking;
use App\User;
use PDF;

class InvoiceController extends Controller
{
    public function show(Request $request, $id)
    {
        // validate $id is number
        if (!is_numeric($id)) {
            return response()->json(["message" => "Invalid id. Id needs to be a number"], 400);
        }

        // only allow if belongs or is admin
        if ($request->user()->role === 'member') {
            if (!$this->isUser($id, $request->user()->id)) {
                return response()->json(['message' => 'The Invoice Does not belongs to you.'], 403);
            }
        }

        $invoice = Invoice::where('booking_id', $id)->first();
        if (!$invoice) {
            return response()->json(['message' => 'Invoice Not Found'], 404);
        }
        $lines = InvoiceDetails::where('invoice_id', $invoice['id'])->orderBy('order', 'asc')->get();

        $status = InvoiceStatuses::where('invoice_id', $invoice['id'])->orderBy('created_at', 'asc')->get();

        $invoice->lines = $lines;
        $invoice->status = $status;
        return response()->json($invoice, 200);

    }

    public function create(Request $request, $id)
    {
        $user = $request->user();

        // validate $id
        if (!is_numeric($id)) {
            return response()->json(["message" => "Invalid id. Id needs to be a number"], 400);
        }

        $validatedData = $request->validate([
            'address' => 'required',
            'buyer_company' => 'required',
            'contact_no' => 'required',
            'marking' => 'required',
            'reg_no' => 'required',
            'adjustment' => 'required|numeric',
            'lines.*.order' => 'required|numeric',
            'lines.*.stock_code' => 'required',
            'lines.*.description' => 'max:256',
            'lines.*.quantity' => 'required|numeric',
            'lines.*.unit_price_rm' => 'required|numeric',
            'lines.*.unit_price_rmb' => '',
            'lines.*.total' => 'required|numeric',
        ]);
        
        // booking_id belongs to user
        $booking = Booking::where('id', $id)->first();
        if (!$booking) {
            return response()->json(['message' => 'Unable to find booking'], 403);
        }

        // user_id in booking does not match
        if ($booking->user_id !== $user->id && $user->role !== 'admin') {
            return response()->json([
                'message' => 'Booking does not belongs to you.'
            ], 403);
        }

        // Amount match with total
        $subtotalArray = array_map(
            function ($line) { return $line['total']; },
            $validatedData['lines']
        );
        $subtotal = array_reduce($subtotalArray, function ($v1, $v2) {
            return $v1 + $v2;
        });
        $adjustment = $request->adjustment;
        $billing_charges = $booking->billing_charge;
        $total = $subtotal + $adjustment + $billing_charges;

        if (abs($total-$booking->bia) < PHP_FLOAT_EPSILON) {
            return response()->json([
                'message' => 'Booking amount expected is '.$booking->bia.'. Your Invoice amount is '.$total
            ], 400);
        }

        // Write to database if does not exist
        $exist = Invoice::where('booking_id', $id)->first();
        if ($exist) {
            return response()
                    ->json(['message' => 'Invoice exist. Cannot create duplicate invoice. Try edit.'], 400);
        }

        // Generate PO Number - po20200810-001(PO Format)
        $dt = Carbon::now();
        $po_number = 'po'.$dt->format('Ymmdd').'-'.$id;

        $invoice = new Invoice;
        $invoice->amount = $total;
        $invoice->booking_id = (int)$id;
        $invoice->reg_no = $validatedData['reg_no'];
        $invoice->buyer_company = $validatedData['buyer_company'];
        $invoice->address = $validatedData['address'];
        $invoice->contact_no = $validatedData['contact_no'];
        $invoice->po_number = $po_number;
        $invoice->adjustment = $adjustment;
        $invoice->save();

        if (!$invoice) {
            return response()
                    ->json(['message' => 'Unable to Save Data'], 500);
        }

        // Insert invoice_id into lines
        foreach( $validatedData['lines'] as $key => $line) {
            $invoice_details = new InvoiceDetails;
            $invoice_details->order = $line['order'];
            $invoice_details->description = $line['description'];
            $invoice_details->quantity = $line['quantity'];
            $invoice_details->stock_code = $line['stock_code'];
            $invoice_details->invoice_id = $invoice->id;
            $invoice_details->unit_price_rmb = $line['unit_price_rmb'];
            $invoice_details->unit_price_rm = $line['unit_price_rm'];
            $invoice_details->total = $line['total'];

            $invoice_details->save();
        }
        unset($line);

        $invoice->lines = $invoice_details;

        // Post status as pending
        $status = new InvoiceStatuses;
        $status->status = 'pending';
        $status->comment = '';
        $status->invoice_id = $invoice->id;
        $status->save();

        if($invoice_details) {
            return $this->show($request, $id);
        } else {
            return response(500);
        }
    }

    public function edit(Request $request, $id)
    {
        $user = $request->user();

        // validate $id
        if (!is_numeric($id)) {
            return response()->json(["message" => "Invalid id. Id needs to be a number"], 400);
        }

        $validatedData = $request->validate([
            'address' => 'required',
            'buyer_company' => 'required',
            'contact_no' => 'required',
            'marking' => 'required',
            'reg_no' => 'required',
            'adjustment' => 'required|numeric',
            'lines.*.order' => 'required|numeric',
            'lines.*.stock_code' => 'required',
            'lines.*.description' => 'max:256',
            'lines.*.quantity' => 'required|numeric',
            'lines.*.unit_price_rm' => 'required|numeric',
            'lines.*.unit_price_rmb' => '',
            'lines.*.total' => 'required|numeric',
        ]);
        
        // booking_id belongs to user
        $booking = Booking::where('id', $id)->first();
        if (!$booking) {
            return response()->json(['message' => 'Unable to find booking'], 403);
        }

        $invoice = Invoice::where('booking_id', $id)->first();
        if (!$invoice) {
            return response()->json(['message' => 'Invoice Not Found'], 404);
        }

        // role === 'member' && $booking->user_id !== user()->id &&  $currentstatus !== 'request_change')
        if ($request->user()->role === 'member') {
            if ($booking->user_id !== $request->user()->id) {
                return response()->json([
                    'message' => 'Booking does not belongs to you'
                ], 403);
            }
            
            $currentstatus = InvoiceStatuses::where('invoice_id', $invoice['id'])->latest('created_at')->first()->status;
            if ($currentstatus !== 'request_change') {
                return response()->json(['message' => 'Submitted PO can only be changed when status is request_change'], 403);
            }
        }

        // Amount match with total
        $subtotalArray = array_map(
            function ($line) { return $line['total']; },
            $validatedData['lines']
        );
        $subtotal = array_reduce($subtotalArray, function ($v1, $v2) {
            return $v1 + $v2;
        });
        $adjustment = $request->adjustment;
        $billing_charges = $booking->billing_charge;
        $total = $subtotal + $adjustment + $billing_charges;

        if (abs($total-$booking->bia) < PHP_FLOAT_EPSILON) {
            return response()->json([
                'message' => 'Booking amount expected is '.$booking->bia.'. Your Invoice amount is '.$total
            ], 400);
        }

        // Generate PO Number - po20200810-001(PO Format)
        $dt = Carbon::now();
        $po_number = 'po'.$dt->format('Ymd').'-'.$id;

        $invoice->amount = $total;
        $invoice->booking_id = (int)$id;
        $invoice->reg_no = $validatedData['reg_no'];
        $invoice->buyer_company = $validatedData['buyer_company'];
        $invoice->address = $validatedData['address'];
        $invoice->contact_no = $validatedData['contact_no'];
        $invoice->po_number = $po_number;
        $invoice->adjustment = $adjustment;
        $invoice->save();

        if (!$invoice) {
            return response()
                    ->json(['message' => 'Unable to Save Data'], 500);
        }

        // Remove all invoice_details corresponding to invoice_id
        $invoice_details = InvoiceDetails::where('invoice_id', $invoice->id);
        $invoice_details->delete();

        // Insert invoice_id into lines
        foreach( $validatedData['lines'] as $key => $line) {
            $invoice_details = new InvoiceDetails;
            $invoice_details->order = $line['order'];
            $invoice_details->description = $line['description'];
            $invoice_details->quantity = $line['quantity'];
            $invoice_details->stock_code = $line['stock_code'];
            $invoice_details->invoice_id = $invoice->id;
            $invoice_details->unit_price_rmb = $line['unit_price_rmb'];
            $invoice_details->unit_price_rm = $line['unit_price_rm'];
            $invoice_details->total = $line['total'];

            $invoice_details->save();
        }
        unset($line);

        // Post status as pending
        $status = new InvoiceStatuses;
        $status->status = 'pending';
        if ($request->user()->role === 'admin') {
            $status->comment = 'Edited by Admin.';
        } else {
            $status->comment = 'Edited by Member.';
        }
        $status->invoice_id = $invoice->id;
        $status->save();

        if($invoice_details) {
            return $this->show($request, $id);
        } else {
            return response(500);
        }
    }

    public function postStatus(Request $request, $id)
    {

        // validate $id is integer
        if (!is_numeric($id)) {
            return response()->json(["message" => "Invalid id. Id needs to be a number"], 400);
        }

        $validatedData = $request->validate([
            'status' => ['required', Rule::in('request_change', 'approve')],
            'comment' => Rule::requiredIf($request->status === 'request_change')
        ]);

        // Save status to database
        $status = new InvoiceStatuses;
        $status->status = $validatedData['status'];
        $status->comment = $validatedData['comment'];
        $status->invoice_id = Invoice::where('booking_id', $id)->first()['id'];
        $status->save();

        $booking = Booking::find($id);

        // Generate EI and EDO only if not exist
        $invoice = Invoice::where('booking_id', $id)->first();
        var_dump($invoice->ei);
        
        if (!$invoice->ei && !$invoice->edo) {

            

            $monthlycount = Invoice::where('ei', 'like', 'EI-'.Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('Ym').'%')->count() + 1;
            $generatednumber = Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('Ym').'-'.sprintf("%05d", $monthlycount);
            $invoice->ei = 'EI-'.$generatednumber;
            $invoice->edo = 'EDO-'.$generatednumber;

            // Skip to next count if already exist
            while (Invoice::where('ei', $invoice->ei)->exists() || Invoice::where('edo', $invoice->edo)->exists()) {
                $monthlycount = $monthlycount + 1;
                $generatednumber = Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('Ym').'-'.sprintf("%05d", $monthlycount);
                $invoice->ei = 'EI-'.$generatednumber;
                $invoice->edo = 'EDO-'.$generatednumber;
            }
            $invoice->save();
        }

        if(!$status) {
            return response()->json(['message' => 'Unable to Update Status'], 500);
        }

        // Change Status to 7 if approve
        // Add email event here
        if ($validatedData['status'] === 'approve') {

            $booking = Booking::findOrFail($id);
            $booking->status = 7;
            $booking->admin_status = 7;
            $booking->save();

            if(!$booking) {
                return response()->json(['message' => 'Unable to Complete Booking. Please Approve Again'], 500);
            }


        }

        // Response
        return response()->json($status, 201);


    }

    public function customerToCIEFPO(Request $request, $id)
    {
        if (!is_numeric($id)) {
            return response()->json(["message" => "Invalid id. Id needs to be a number"], 400);
        }

        // Booking Exist and Belongs to User or is Admin
        if ($request->user()->role === 'member') {
            if (!$this->isUser($id, $request->user()->id)) {
                return response()->json(['message' => 'The PO Does not belongs to you.'], 403);
            }
        }

        // Generate PO

        $booking = Booking::where('id', $id)->first();
        $invoice = Invoice::where('booking_id', $booking['id'])->first();
        $lines = InvoiceDetails::where('invoice_id', $invoice->id)->get();
        $marking = User::find($booking->user_id)->marking;
        $subtotal = InvoiceDetails::where('invoice_id', $invoice->id)->sum('total');
        $adjustment = $invoice->adjustment;
        $billing_charges = $booking->billing_charge;
        $total = $subtotal + $adjustment + $billing_charges;

        $data = [
            'title' => 'Purchase Order',
            'detail' => [
                'date' => Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('d-m-Y'),
                'po' => $invoice->po_number,
                'do' => '',
                'ref' => $booking->id
            ],
            'buyer' => [
                'buyer_company' => $invoice->buyer_company,
                'reg_no' => $invoice->reg_no,
                'address' => $invoice->address,
                'gst' => '',
                'phone' => $invoice->contact_no,
                'marking_no' => $marking
            ],
            'seller' => [
                'seller_company' => 'CIEF Worldwide Sdn Bhd (1134596-M)',
                'address' => 'Malaysia Global Innovation & Creativity Centre, Level
                1 CWS, Block 3730, Persiaran APEC 63000
                Cyberjaya.',
                'phone' => '018 2909252',
            ],
            'lines' => $lines,
            'subtotal' => $subtotal,
            'adjustment' => $adjustment,
            'billingcharges' => $billing_charges,
            'total' => $total
        ];
       
        $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
        $fontDirs = $defaultConfig['fontDir'];
        $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
        $fontData = $defaultFontConfig['fontdata'];

        $mpdf = new \Mpdf\Mpdf([
            'fontDir' => array_merge($fontDirs, [
                __DIR__ . '/custom/font/directory',
            ]),
        ]);
        $mpdf->shrink_tables_to_fit=1;
        $mpdf->keep_table_proportions = true;
        $mpdf->SetTitle('Invoice');

        $html = view('pdf/po', $data);
        
        $mpdf->Bookmark('Start of the document');
        $mpdf->SetHTMLFooter('
        <table width="100%">
            <tr>
                <td style="text-align: right; ">This is generated by computer. No signature required.</td>
                <td style="text-align: right; ">Page {PAGENO} of {nbpg}</td>
            </tr>
        </table>
        ');
        $mpdf->setHeader($invoice->po_number);
        $mpdf->WriteHTML($html);

        $mpdf->Output();

    }

    public function CIEFToSupplierDO(Request $request, $id)
    {

        if (!is_numeric($id)) {
            return response()->json(["message" => "Invalid id. Id needs to be a number"], 400);
        }

        // Booking Exist and Belongs to User or is Admin
        if ($request->user()->role === 'member') {
            if (!$this->isUser($id, $request->user()->id)) {
                return response()->json(['message' => 'The PO Does not belongs to you.'], 403);
            }
        }

        // Ensure Invoice Status is Approve
        // if ($this->invoiceStatus($id) !== 'approve') {
        //     return response()->json(['message' => 'Invoice must and exist and approve status.'], 403);
        // }

        // Generate PO

        $booking = Booking::where('id', $id)->first();
        $invoice = Invoice::where('booking_id', $booking['id'])->first();
        $supplierbooking = SupplierBooking::where('booking_id', $id)->first();
        $lines = DB::table('invoices')
                    ->join('invoice_details', function ($join) use ($id) {
                        $join->where('invoices.booking_id', '=', $id);
                        $join->on('invoice_details.invoice_id', '=', 'invoices.id');
                    })
                    ->join('supplier_bookings', 'invoices.booking_id', '=', 'supplier_bookings.booking_id')
                    ->select(
                        'order',
                        'stock_code',
                        'description',
                        'quantity',
                        DB::raw('ROUND((invoice_details.unit_price_rmb / supplier_bookings.rate), 2) as unit_price_rm'),
                        DB::raw('ROUND((invoice_details.quantity * ROUND((invoice_details.unit_price_rmb / supplier_bookings.rate), 2)), 2) as total')
                        // subtotal
                        // adjustment
                        // total
                    )->get();
        $supplierbooking = SupplierBooking::where('booking_id', $id)->first();
        $supplier = SettingSupplier::where('id', $supplierbooking->supplier_id)->first();
        $subtotal = $lines->sum('total');
        $billing_charges = null;
        $total = $supplierbooking->amount_after_tax;
        $adjustment = $total - $subtotal;

        $data = [
            'title' => 'Delivery Order',
            'detail' => [
                'date' => Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('d-m-Y'),
                'po' => $invoice->po_number,
                'do' => '',
                'ref' => $booking->id
            ],
            'buyer' => [
                'buyer_company' => 'CIEF Worldwide Sdn Bhd',
                'reg_no' => '1134596-M',
                'address' => 'Malaysia Global Innovation & Creativity Centre, Level
                                1 CWS, Block 3730, Persiaran APEC 63000
                                Cyberjaya.',
                'gst' => '',
                'phone' => '018 2909252',
                'marking_no' => ''
            ],
            'seller' => [
                'seller_company' => $supplier->company_name,
                'address' => '',
                'phone' => '',
            ],
            'lines' => $lines,
            'subtotal' => $subtotal,
            'adjustment' => $adjustment,
            'billingcharges' => $billing_charges,
            'total' => $total
        ];
        $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
        $fontDirs = $defaultConfig['fontDir'];
        $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
        $fontData = $defaultFontConfig['fontdata'];

        $mpdf = new \Mpdf\Mpdf([
            'fontDir' => array_merge($fontDirs, [
                __DIR__ . '/custom/font/directory',
            ]),
        ]);
        $mpdf->shrink_tables_to_fit=1;
        $mpdf->keep_table_proportions = true;
        $mpdf->SetTitle('Invoice');

        $html = view('pdf/po', $data);
        
        $mpdf->Bookmark('Start of the document');
        $mpdf->SetHTMLFooter('
        <table width="100%">
            <tr>
                <td style="text-align: right; ">This is generated by computer. No signature required.</td>
                <td style="text-align: right; ">Page {PAGENO} of {nbpg}</td>
            </tr>
        </table>
        ');
        $mpdf->setHeader($invoice->po_number);
        $mpdf->WriteHTML($html);

        $mpdf->Output();

    }

    public function CIEFToCustomerInvoice(Request $request, $id)
    {
       
        if (!is_numeric($id)) {
            return response()->json(["message" => "Invalid id. Id needs to be a number"], 400);
        }

        // only allow if belongs or is admin
        if ($request->user()->role === 'member') {
            if (!$this->isUser($id, $request->user()->id)) {
                return response()->json(['message' => 'The Invoice Does not belongs to you.'], 403);
            }
        }

        $booking = Booking::where('id', $id)->first();
        $invoice = Invoice::where('booking_id', $booking['id'])->first();
        $lines = InvoiceDetails::where('invoice_id', $invoice->id)->get();
        $subtotal = InvoiceDetails::where('invoice_id', $invoice->id)->sum('total');
        $adjustment = $invoice->adjustment;
        $billing_charges = $booking->billing_charge;
        $total = $subtotal + $adjustment + $billing_charges;


        $data = [
            'title' => 'Invoice',
            'detail' => [
                'date' => Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('d-m-Y'),
                'ei' => $invoice->ei,
                'edo' => null,
                'ref' => $booking->id
            ],
            'billto' => [
                'buyer_company' => $invoice->buyer_company,
                'reg_no' => $invoice->reg_no,
                'address' => $invoice->address,
                'gst' => '',
                'phone' => $invoice->contact_no,
                'marking_no' => ''
            ],
            'total_page' => (count($lines) / 10),
            'lines' => $lines,
            'subtotal' => $subtotal,
            'adjustment' => $adjustment,
            'billingcharges' => $billing_charges,
            'total' => $total
        ];

        $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
        $fontDirs = $defaultConfig['fontDir'];
        $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
        $fontData = $defaultFontConfig['fontdata'];

        $mpdf = new \Mpdf\Mpdf([
            'fontDir' => array_merge($fontDirs, [
                __DIR__ . '/custom/font/directory',
            ]),
        ]);
        $mpdf->shrink_tables_to_fit=1;
        $mpdf->keep_table_proportions = true;
        $mpdf->SetTitle('Invoice');

        $html = view('pdf/invoice', $data);
        
        $mpdf->Bookmark('Start of the document');
        $mpdf->SetHTMLFooter('
        <table width="100%">
            <tr>
                <td style="text-align: right; ">This is generated by computer. No signature required.</td>
                <td style="text-align: right; ">Page {PAGENO} of {nbpg}</td>
            </tr>
        </table>
        ');
        $mpdf->setHeader($invoice->ei);
        $mpdf->WriteHTML($html);

        $mpdf->Output();
    }

    public function CIEFToCustomerDO(Request $request, $id)
    {
        if (!is_numeric($id)) {
            return response()->json(["message" => "Invalid id. Id needs to be a number"], 400);
        }

        // only allow if belongs or is admin
        if ($request->user()->role === 'member') {
            if (!$this->isUser($id, $request->user()->id)) {
                return response()->json(['message' => 'The Invoice Does not belongs to you.'], 403);
            }
        }

         // Generate PO

        $booking = Booking::where('id', $id)->first();
        $invoice = Invoice::where('booking_id', $booking['id'])->first();
        $lines = InvoiceDetails::where('invoice_id', $invoice->id)->get();
        $subtotal = InvoiceDetails::where('invoice_id', $invoice->id)->sum('total');
        $adjustment = $invoice->adjustment;
        $billing_charges = $booking->billing_charge;
        $total = $subtotal + $adjustment + $billing_charges;


        $data = [
            'title' => 'Delivery Order',
            'detail' => [
                'date' => Carbon::createFromFormat('Y-m-d H:i:s', $booking->created_at)->format('d-m-Y'),
                'ei' => '',
                'edo' => $invoice->edo,
                'ref' => $booking->id
            ],
            'billto' => [
                'buyer_company' => $invoice->buyer_company,
                'reg_no' => $invoice->reg_no,
                'address' => $invoice->address,
                'gst' => '',
                'phone' => $invoice->contact_no,
                'marking_no' => ''
            ],
            'lines' => $lines,
            'subtotal' => $subtotal,
            'adjustment' => $adjustment,
            'billingcharges' => $billing_charges,
            'total' => $total
        ];

        $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
        $fontDirs = $defaultConfig['fontDir'];
        $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
        $fontData = $defaultFontConfig['fontdata'];


        $mpdf = new \Mpdf\Mpdf([
            'fontDir' => array_merge($fontDirs, [
                __DIR__ . '/custom/font/directory',
            ]),
        ]);
        $mpdf->shrink_tables_to_fit=1;
        $mpdf->keep_table_proportions = true;
        $mpdf->SetTitle('do');
        
        $html = view('pdf/invoice', $data);
        
        $mpdf->Bookmark('Start of the document');
        $mpdf->SetHTMLFooter('
        <table width="100%">
            <tr>
                <td style="text-align: right; ">This is generated by computer. No signature required.</td>
                <td style="text-align: right; ">Page {PAGENO} of {nbpg}</td>
            </tr>
        </table>
        ');
        $mpdf->setHeader($invoice->edo);
        $mpdf->WriteHTML($html);

        $mpdf->Output();
    }


    private function isUser($booking_id, $user_id) {
        
        $booking = Booking::where([
            ['id', '=', $booking_id],
            ['user_id', '=', $user_id]
        ])->first();
        
        if ($booking) {
            return true;
        } else {
            return false;
        }
    }

    private function invoiceStatus($booking_id) {
        $invoice = Invoice::where('booking_id', $booking_id)->first();
        if (!$invoice) {
            return;
        }
        $currentstatus = InvoiceStatuses::where('invoice_id', $invoice['id'])->latest('created_at')->first()->status;
        return $currentstatus;
    }

}
