<?php

namespace App\Models\Traits;

use App\Models\Client;
use App\Models\Invoice;
use Auth;
use Carbon;

/**
 * Class GeneratesNumbers.
 */
trait GeneratesNumbers
{
    /**
     * @param $entity
     *
     * @return mixed|string
     */
    public function getNextNumber($entity = false)
    {
        $entity = $entity ?: new Client();
        $entityType = $entity->getEntityType();

        $counter = $this->getCounter($entityType);
        $prefix = $this->getNumberPrefix($entityType);
        $counterOffset = 0;
        $check = false;
        $lastNumber = false;

        if ($entityType == ENTITY_CLIENT && ! $this->clientNumbersEnabled()) {
            return '';
        }

        // confirm the invoice number isn't already taken
        do {
            if ($this->hasNumberPattern($entityType)) {
                $number = $this->applyNumberPattern($entity, $counter);
            } else {
                $number = $prefix . str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);
            }

            if ($entity->recurring_invoice_id) {
                $number = $this->recurring_invoice_number_prefix . $number;
            }

            if ($entity->isEntityType(ENTITY_CLIENT)) {
                $check = Client::scope(false, $this->id)->whereIdNumber($number)->withTrashed()->first();
            } else {
                $check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first();
            }
            $counter++;
            $counterOffset++;

            // prevent getting stuck in a loop
            if ($number == $lastNumber) {
                return '';
            }
            $lastNumber = $number;

        } while ($check);

        // update the counter to be caught up
        if ($counterOffset > 1) {
            $this->syncOriginal();
            if ($entity->isEntityType(ENTITY_CLIENT)) {
                if ($this->clientNumbersEnabled()) {
                    $this->client_number_counter += $counterOffset - 1;
                    $this->save();
                }
            } elseif ($entity->isEntityType(ENTITY_CREDIT)) {
                if ($this->creditNumbersEnabled()) {
                    $this->credit_number_counter += $counterOffset - 1;
                    $this->save();
                }
            } elseif ($entity->isType(INVOICE_TYPE_QUOTE)) {
                if (! $this->share_counter) {
                    $this->quote_number_counter += $counterOffset - 1;
                    $this->save();
                }
            } else {
                $this->invoice_number_counter += $counterOffset - 1;
                $this->save();
            }
        }

        return $number;
    }

    /**
     * @param $entityType
     *
     * @return string
     */
    public function getNumberPrefix($entityType)
    {
        if (! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
            return '';
        }

        $field = "{$entityType}_number_prefix";

        return $this->$field ?: '';
    }

    /**
     * @param $entityType
     *
     * @return bool
     */
    public function getNumberPattern($entityType)
    {
        if (! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
            return false;
        }

        $field = "{$entityType}_number_pattern";

        return $this->$field;
    }

    /**
     * @param $entityType
     *
     * @return bool
     */
    public function hasNumberPattern($entityType)
    {
        return $this->getNumberPattern($entityType) ? true : false;
    }

    /**
     * @param $entityType
     * @param mixed $invoice
     *
     * @return string
     */
    public function hasClientNumberPattern($invoice)
    {
        if (! $this->isPro()) {
            return false;
        }

        $pattern = $invoice->invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_pattern : $this->invoice_number_pattern;

        return strstr($pattern, '$client') !== false || strstr($pattern, '$idNumber') !== false;
    }

    /**
     * @param $entity
     * @param mixed $counter
     *
     * @return bool|mixed
     */
    public function applyNumberPattern($entity, $counter = 0)
    {
        $entityType = $entity->getEntityType();
        $counter = $counter ?: $this->getCounter($entityType);
        $pattern = $this->getNumberPattern($entityType);

        if (! $pattern) {
            return false;
        }

        $search = ['{$year}'];
        $replace = [date('Y')];

        $search[] = '{$counter}';
        $replace[] = str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);

        if (strstr($pattern, '{$userId}')) {
            $userId = $entity->user ? $entity->user->public_id : (Auth::check() ? Auth::user()->public_id : 0);
            $search[] = '{$userId}';
            $replace[] = str_pad(($userId + 1), 2, '0', STR_PAD_LEFT);
        }

        $matches = false;
        preg_match('/{\$date:(.*?)}/', $pattern, $matches);
        if (count($matches) > 1) {
            $format = $matches[1];
            $search[] = $matches[0];
            //$date = date_create()->format($format);
            $date = Carbon::now(session(SESSION_TIMEZONE, DEFAULT_TIMEZONE))->format($format);
            $replace[] = str_replace($format, $date, $matches[1]);
        }

        $pattern = str_replace($search, $replace, $pattern);
        $pattern = $this->getClientInvoiceNumber($pattern, $entity);

        return $pattern;
    }

    /**
     * @param $pattern
     * @param $invoice
     *
     * @return mixed
     */
    private function getClientInvoiceNumber($pattern, $invoice)
    {
        if (! $invoice->client_id) {
            return $pattern;
        }

        $search = [
            '{$custom1}',
            '{$custom2}',
            '{$idNumber}',
            '{$clientCustom1}',
            '{$clientCustom2}',
            '{$clientIdNumber}',
            '{$clientCounter}',
        ];

        $client = $invoice->client;
        $clientCounter = ($invoice->isQuote() && ! $this->share_counter) ? $client->quote_number_counter : $client->invoice_number_counter;

        $replace = [
            $client->custom_value1,
            $client->custom_value2,
            $client->id_number,
            $client->custom_value1, // backwards compatibility
            $client->custom_value2,
            $client->id_number,
            str_pad($clientCounter, $this->invoice_number_padding, '0', STR_PAD_LEFT),
        ];

        return str_replace($search, $replace, $pattern);
    }

    /**
     * @param $entityType
     *
     * @return mixed
     */
    public function getCounter($entityType)
    {
        if ($entityType == ENTITY_CLIENT) {
            return $this->client_number_counter;
        } elseif ($entityType == ENTITY_CREDIT) {
            return $this->credit_number_counter;
        } elseif ($entityType == ENTITY_QUOTE && ! $this->share_counter) {
            return $this->quote_number_counter;
        } else {
            return $this->invoice_number_counter;
        }
    }

    /**
     * @param $entityType
     *
     * @return mixed|string
     */
    public function previewNextInvoiceNumber($entityType = ENTITY_INVOICE)
    {
        $client = \App\Models\Client::scope()->first();

        $invoice = $this->createInvoice($entityType, $client ? $client->id : 0);

        return $this->getNextNumber($invoice);
    }

    /**
     * @param $entity
     */
    public function incrementCounter($entity)
    {
        if ($entity->isEntityType(ENTITY_CLIENT)) {
            if ($this->client_number_counter > 0) {
                $this->client_number_counter += 1;
            }
            $this->save();
            return;
        } elseif ($entity->isEntityType(ENTITY_CREDIT)) {
            if ($this->credit_number_counter > 0) {
                $this->credit_number_counter += 1;
            }
            $this->save();
            return;
        }

        if ($this->usesClientInvoiceCounter()) {
            if ($entity->isType(INVOICE_TYPE_QUOTE) && ! $this->share_counter) {
                $entity->client->quote_number_counter += 1;
            } else {
                $entity->client->invoice_number_counter += 1;
            }
            $entity->client->save();
        }

        if ($this->usesInvoiceCounter()) {
            if ($entity->isType(INVOICE_TYPE_QUOTE) && ! $this->share_counter) {
                $this->quote_number_counter += 1;
            } else {
                $this->invoice_number_counter += 1;
            }
            $this->save();
        }
    }

    public function usesInvoiceCounter()
    {
        return ! $this->hasNumberPattern(ENTITY_INVOICE) || strpos($this->invoice_number_pattern, '{$counter}') !== false;
    }

    public function usesClientInvoiceCounter()
    {
        return strpos($this->invoice_number_pattern, '{$clientCounter}') !== false;
    }

    public function clientNumbersEnabled()
    {
        return $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $this->client_number_counter > 0;
    }

    public function creditNumbersEnabled()
    {
        return $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $this->credit_number_counter > 0;
    }

    public function checkCounterReset()
    {
        if (! $this->reset_counter_frequency_id || ! $this->reset_counter_date) {
            return false;
        }

        $timezone = $this->getTimezone();
        $resetDate = Carbon::parse($this->reset_counter_date, $timezone);

        if (! $resetDate->isToday()) {
            return false;
        }

        switch ($this->reset_counter_frequency_id) {
            case FREQUENCY_WEEKLY:
                $resetDate->addWeek();
                break;
            case FREQUENCY_TWO_WEEKS:
                $resetDate->addWeeks(2);
                break;
            case FREQUENCY_FOUR_WEEKS:
                $resetDate->addWeeks(4);
                break;
            case FREQUENCY_MONTHLY:
                $resetDate->addMonth();
                break;
            case FREQUENCY_TWO_MONTHS:
                $resetDate->addMonths(2);
                break;
            case FREQUENCY_THREE_MONTHS:
                $resetDate->addMonths(3);
                break;
            case FREQUENCY_FOUR_MONTHS:
                $resetDate->addMonths(4);
                break;
            case FREQUENCY_SIX_MONTHS:
                $resetDate->addMonths(6);
                break;
            case FREQUENCY_ANNUALLY:
                $resetDate->addYear();
                break;
            case FREQUENCY_TWO_YEARS:
                $resetDate->addYears(2);
                break;
        }

        $this->reset_counter_date = $resetDate->format('Y-m-d');
        $this->invoice_number_counter = 1;
        $this->quote_number_counter = 1;
        $this->credit_number_counter = $this->credit_number_counter > 0 ? 1 : 0;
        $this->save();
    }
}
