<?php
namespace Hilco\Models;
use Aws\S3\S3Client;
use Carbon\Traits\Creator;
use DB;
use Carbon\Carbon;
use Hilco\Pricing\PriceHelper;
use HilcoUAC\Exports\ArrayExport;
use HilcoUAC\Http\Controllers\PriceChecksToolController;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Hilco\HasSharedCustomLoggingTrait;
use ZipArchive;
use Response;
use Excel;


/**
 * @property string $name
 * @property bool $isWhitelist
 * @property bool $inStock
 * @property string $imageHeight
 * @property string $imageWidth
 * @property bool $enabled
 * @property string $password
 * @property string $username
 * @property bool $user_created
 * @property string $imageNameFormat
 * @property bool $includeImages
 * @property string $server_root
 * @property string $default_language
 * @property string $default_date_formatter
 * @property int $customer_id
 * @property string $visibility
 * @property string $autoship
 * @property string $private_label
 * @property string $iny_prescription
 * @property string $export_type
 * @property string $available_plants
 * @property bool $exclude_plant
 * @property bool $exclude_hierarchy
 * @property bool $exclude_group
 * @property bool $exclude_category
 * @property bool $exclude_collection
 * @property bool $exclude_family
 * @property string $filename
 * @property string $zipname
 * @property string $csv_delimiter
 * @property bool $csv_includeheader
 * @property string $xlsx_sheetname
 * @property string $notes
 * @property string $division_tag
 * @property string $email_on_run
 * @property string $email_from
 * @property bool $include_file_in_email
 * @property int $datafeedemailtemplate_id
 * @property bool $ftp_enabled
 * @property bool $email_enabled
 * @property string $ftp_port
 * @property string $ftp_host
 * @property string $ftp_user
 * @property string $ftp_pass
 * @property bool $ftp_passive
 * @property string $ftp_root
 * @property bool $ftp_secure
 * @property bool $autorun_enabled
 * @property string $autorun_startdate
 * @property string $autorun_enddate
 * @property int $autorun_hour
 * @property int $autorun_minute
 * @property string $autorun_cadence
 * @property int $autorun_weekly_frequency
 * @property string $autorun_weekly_weekdays
 * @property int $sort_datafeedfield_id
 * @property string $sort_direction
 * @property int $sort_sequence_number
 * @property string $xml_headertagname
 * @property string $xml_itemtagname
 */
class Datafeed extends WebModel {

    const SHORTNAME = "SHORTNAME";
    const STYLENUMBER = "STYLENUMBER";
    const SHORTNAME_NO_F = "SHORTNAME_NO_F";
    const STYLENUMBER_NO_F = "STYLENUMBER_NO_F";
    const SAFILO_STANDARD = "SAFILO_STANDARD";
    const UPC = "UPC";

    protected $table = 'Datafeeds';
    protected $fillable = [
        'id', 'name', 'customer_id',
        'includeImages', 'isWhitelist',
        'imageNameFormat', 'enabled', 'password'
    ];

    public function datafeedJobs() {
        return $this->hasMany(DatafeedJob::class);
    }

    public function datafeedEmailTemplate() {
        return $this->belongsTo(DatafeedEmailTemplate::class, 'datafeedemailtemplate_id', 'id');
    }

    public function latestUnfailedJob() {
        return $this->hasOne(DatafeedJob::class)->where('success', '!=', -1)->orderBy('submit_time', 'desc')->limit(1);
    }

    public function brands()
    {
        return $this->belongsToMany(Brand::class, 'Datafeed_Brand', 'datafeed_id', 'brand_id');
    }

    public function webattributefilters()
    {
        return $this->hasMany(Datafeed_WebAttribute::class);
    }

    public function customer() {
        return $this->belongsTo(Customer::class);
    }

    public function datafeedFields()
    {
        return $this->hasMany(DatafeedField_Datafeed::class)->with('datafeedfield.translations')->orderBy('overrideOrder');
    }

    public function datafeed_webhierarchy()
    {
        return $this->hasMany(Datafeed_WebHierarchy::class);
    }

    public function datafeed_webcategory()
    {
        return $this->hasMany(Datafeed_WebCategory::class);
    }

    public function datafeed_webgroup()
    {
        return $this->hasMany(Datafeed_WebGroup::class);
    }

    public function datafeed_webcollection()
    {
        return $this->hasMany(Datafeed_WebCollection::class);
    }

    public function datafeed_webfamily()
    {
        return $this->hasMany(Datafeed_WebFamily::class);
    }

    public function getImageNameFormatAttribute(){
        if (!in_array($this->getOriginal('imageNameFormat'), [self::SHORTNAME, self::SHORTNAME_NO_F, self::STYLENUMBER, self::STYLENUMBER_NO_F, self::SAFILO_STANDARD, self::UPC])){
            return self::SHORTNAME;
        }else{
            return $this->getOriginal('imageNameFormat');
        }
    }

    public function imageNameFormatIsSelected($imageNameFormat){
        return $this->imageNameFormat === $imageNameFormat;
    }
    public function getImageWidthAttribute(){
        $imageWidth = intval($this->getOriginal('imageWidth'));
        if ($imageWidth <= 0){
            return -1;
        }else{
            return $imageWidth;
        }
    }

    public function getImageHeightAttribute(){
        $imageHeight = intval($this->getOriginal('imageHeight'));
        if ($imageHeight <= 0){
            return -1;
        }else{
            return $imageHeight;
        }
    }

    public function includesInventoryCode($inventoryCode){
        return in_array($inventoryCode, explode(",", $this->inventoryCodes));
    }

    public function includesRankCode($rankCode){
        return in_array($rankCode, explode(",", $this->rankCodes));
    }

    public function buildQuery(){

        $query = WebPart
            ::join('Parts', 'WebParts.part_id', '=', 'Parts.id')
            ->whereRaw('Parts.deleted_at = 0')
            ->join('WebPart_WebFamily', 'WebParts.id', 'WebPart_WebFamily.webpart_id')
            ->whereRaw('WebPart_WebFamily.deleted_at = 0')
            ->join('WebFamilies', 'WebPart_WebFamily.webfamily_id', 'WebFamilies.id')
            ->whereRaw('WebFamilies.deleted_at = 0')
            ->join('WebFamily_WebCollection', 'WebFamily_WebCollection.webfamily_id', 'WebFamilies.id')
            ->whereRaw('WebFamily_WebCollection.deleted_at = 0')
            ->join('WebCollections', 'WebFamily_WebCollection.webcollection_id', 'WebCollections.id')
            ->whereRaw('WebCollections.deleted_at = 0')
            ->join('WebCollection_WebCategory', 'WebCollection_WebCategory.webcollection_id', 'WebCollections.id')
            ->whereRaw('WebCollection_WebCategory.deleted_at = 0')
            ->join('WebCategories', 'WebCollection_WebCategory.webcategory_id', 'WebCategories.id')
            ->whereRaw('WebCategories.deleted_at = 0')
            ->join('WebCategory_WebGroup', 'WebCategory_WebGroup.webcategory_id', 'WebCategories.id')
            ->whereRaw('WebCategory_WebGroup.deleted_at = 0')
            ->join('WebGroups', 'WebCategory_WebGroup.webgroup_id', 'WebGroups.id')
            ->whereRaw('WebGroups.deleted_at = 0')
            ->join('WebGroup_WebHierarchy', 'WebGroup_WebHierarchy.webgroup_id', 'WebGroups.id')
            ->whereRaw('WebGroup_WebHierarchy.deleted_at = 0')
            ->join('WebHierarchies', 'WebGroup_WebHierarchy.webhierarchy_id', 'WebHierarchies.id')
            ->whereRaw('WebHierarchies.deleted_at = 0')
            ->when($this->visibility == 'web', function ($query)  {
                if ($this->customer != null) {
                    $websilo = $this->customer->websilo;
                    $plant = $this->customer->activeSegment->plant;
                    $query->webVisible($websilo, $plant);    
                }
                
            })
            ->when(!$this->datafeed_webhierarchy->isEmpty(), function ($query) {
                $ids = [];
                $bindings = [];
                foreach($this->datafeed_webhierarchy as $datafeed_webhierarchy) {
                    $ids[] = $datafeed_webhierarchy->webhierarchy_id;
                    $bindings[] = "?";
                }
                if ($this->exclude_hierarchy == 0) {
                    $query->whereRaw("WebHierarchies.id in (".join(",", $bindings).")", $ids);
                } else {
                    $query->whereRaw("WebHierarchies.id not in (".join(",", $bindings).")", $ids);
                }

            })
            ->when(!$this->datafeed_webgroup->isEmpty(), function ($query) {
                $ids = [];
                $bindings = [];
                foreach($this->datafeed_webgroup as $datafeed_webgroup) {
                    $ids[] = $datafeed_webgroup->webgroup_id;
                    $bindings[] = "?";
                }
                if ($this->exclude_group == 0) {
                    $query->whereRaw("WebGroups.id in (" . join(",", $bindings) . ")", $ids);
                } else {
                    $query->whereRaw("WebGroups.id not in (" . join(",", $bindings) . ")", $ids);

                }
            })
            ->when(!$this->datafeed_webcategory->isEmpty(), function ($query) {
                $ids = [];
                $bindings = [];
                foreach($this->datafeed_webcategory as $datafeed_webcategory) {
                    $ids[] = $datafeed_webcategory->webcategory_id;
                    $bindings[] = "?";
                }
                if ($this->exclude_category == 0) {
                    $query->whereRaw("WebCategories.id in (".join(",", $bindings).")", $ids);
                } else {
                    $query->whereRaw("WebCategories.id not in (".join(",", $bindings).")", $ids);
                }

            })
            ->when(!$this->datafeed_webcollection->isEmpty(), function ($query) {
                $ids = [];
                $bindings = [];
                foreach($this->datafeed_webcollection as $datafeed_webcollection) {
                    $ids[] = $datafeed_webcollection->webcollection_id;
                    $bindings[] = "?";
                }
                if ($this->exclude_collection == 0) {
                    $query->whereRaw("WebCollections.id in (".join(",", $bindings).")", $ids);
                } else {
                    $query->whereRaw("WebCollections.id not in (".join(",", $bindings).")", $ids);
                }
            })
            ->when(!$this->datafeed_webfamily->isEmpty(), function ($query) {
                $ids = [];
                $bindings = [];
                foreach($this->datafeed_webfamily as $datafeed_webfamily) {
                    $ids[] = $datafeed_webfamily->webfamily_id;
                    $bindings[] = "?";
                }
                if ($this->exclude_family == 0) {
                    $query->whereRaw("WebFamilies.id in (".join(",", $bindings).")", $ids);
                } else {
                    $query->whereRaw("WebFamilies.id not in (".join(",", $bindings).")", $ids);
                }

            })
            ->when($this->autoship == 'yes', function ($query) {
                $query->whereRaw("WebParts.has_subscribe_and_save = 1");
            })
            ->when($this->autoship == 'no', function ($query) {
                $query->whereRaw("WebParts.has_subscribe_and_save = 0");
            })
            ->when($this->private_label == 'yes', function ($query) {
                $query->whereRaw("Parts.private_lbl = 1");
            })
            ->when($this->private_label == 'no', function ($query) {
                $query->whereRaw("Parts.private_lbl = 0");
            })
            ->when($this->iny_prescription == 'yes', function ($query) {
                $query->whereRaw("WebFamilies.is_prescription = 1");
            })
            ->when($this->iny_prescription == 'no', function ($query) {
                $query->whereRaw("WebFamilies.is_prescription = 0");
            })
            ->when($this->sort_datafeedfield_id != null, function ($query) {
                $datafeedfield = DatafeedField_Datafeed::where('datafeed_id', $this->id)->where('datafeedfield_id', $this->sort_datafeedfield_id)->where('sequence_number', $this->sort_sequence_number)->first();
                if ($datafeedfield != null) {
                    $query->orderBy($datafeedfield->fieldNameWithId, $this->sort_direction);
                }
            })
            ;

        if ($this->available_plants != '') {
            foreach(explode(",",$this->available_plants) as $plant) {
                $query = $query->whereRaw('exists (select 1 from Parts join InventoryItems on (InventoryItems.part_id = Parts.id and InventoryItems.deleted_at = 0) where Parts.id = WebParts.part_id and InventoryItems.plant = ? and part_stat != "Obsolete")', $plant);
            }    
        }

        foreach($this->webattributefilters as $attributeFilter) {
            $language_code = $attributeFilter->language_code;
            if ($language_code == 'default') {
                $language_code = $this->default_language;
            }
            $query = $query->where(function ($q) use ($attributeFilter, $language_code) {
                if ($attributeFilter->operator == 'equals') {
                    $q = $q->whereRaw('exists (select 1 from WebAttribute_WebPart
                                                                join WebModelTranslations on (translatable_type = "webAttributeValue" and column_name = "attribute_value" and translatable_id = WebAttribute_WebPart.id) 
                                                        where WebAttribute_WebPart.webpart_id = WebParts.id
                                                                and WebModelTranslations.`language` = ?
                                                                and WebAttribute_WebPart.webattribute_id = ?
                                                                and WebModelTranslations.translation = ?
                                                                and WebAttribute_WebPart.deleted_at = 0
                                                                and exists (select 1 from WebAttribute_WebFamily
                                                                                    where WebAttribute_WebFamily.webfamily_id = WebFamilies.id
                                                                                    and WebAttribute_WebFamily.webattribute_id = WebAttribute_WebPart.webattribute_id
                                                                                    and WebAttribute_WebFamily.deleted_at = 0
                                                                            )
                                                )', [$language_code, $attributeFilter->webattribute_id, $attributeFilter->value]);
                } else if ($attributeFilter->operator == 'notequal') {
                    $q = $q->whereRaw('exists (select 1 from WebAttribute_WebPart
                                                                join WebModelTranslations on (translatable_type = "webAttributeValue" and column_name = "attribute_value" and translatable_id = WebAttribute_WebPart.id)
                                                        where WebAttribute_WebPart.webpart_id = WebParts.id
                                                                and WebModelTranslations.`language` = ?
                                                                and WebAttribute_WebPart.webattribute_id = ?
                                                                and WebModelTranslations.translation != ?
                                                                and WebAttribute_WebPart.deleted_at = 0
                                                                and exists (select 1 from WebAttribute_WebFamily
                                                                                    where WebAttribute_WebFamily.webfamily_id = WebFamilies.id
                                                                                    and WebAttribute_WebFamily.webattribute_id = WebAttribute_WebPart.webattribute_id
                                                                                    and WebAttribute_WebFamily.deleted_at = 0
                                                                            )
                                                )', [$language_code, $attributeFilter->webattribute_id, $attributeFilter->value]);
                } else if ($attributeFilter->operator == 'contains') {
                    $q = $q->whereRaw('exists (select 1 from WebAttribute_WebPart
                                                                join WebModelTranslations on (translatable_type = "webAttributeValue" and column_name = "attribute_value" and translatable_id = WebAttribute_WebPart.id)
                                                        where WebAttribute_WebPart.webpart_id = WebParts.id
                                                                and WebModelTranslations.`language` = ?
                                                                and WebAttribute_WebPart.webattribute_id = ?
                                                                and WebModelTranslations.translation like ?
                                                                and WebAttribute_WebPart.deleted_at = 0
                                                                and exists (select 1 from WebAttribute_WebFamily
                                                                                    where WebAttribute_WebFamily.webfamily_id = WebFamilies.id
                                                                                    and WebAttribute_WebFamily.webattribute_id = WebAttribute_WebPart.webattribute_id
                                                                                    and WebAttribute_WebFamily.deleted_at = 0
                                                                            )
                                                )', [$language_code, $attributeFilter->webattribute_id, "%" . $attributeFilter->value . "%"]);
                } else if ($attributeFilter->operator == 'starts') {
                    $q = $q->whereRaw('exists (select 1 from WebAttribute_WebPart
                                                                join WebModelTranslations on (translatable_type = "webAttributeValue" and column_name = "attribute_value" and translatable_id = WebAttribute_WebPart.id)
                                                        where WebAttribute_WebPart.webpart_id = WebParts.id
                                                                and WebModelTranslations.`language` = ?
                                                                and WebAttribute_WebPart.webattribute_id = ?
                                                                and WebModelTranslations.translation like ?
                                                                and WebAttribute_WebPart.deleted_at = 0
                                                                and exists (select 1 from WebAttribute_WebFamily
                                                                                    where WebAttribute_WebFamily.webfamily_id = WebFamilies.id
                                                                                    and WebAttribute_WebFamily.webattribute_id = WebAttribute_WebPart.webattribute_id
                                                                                    and WebAttribute_WebFamily.deleted_at = 0
                                                                            )
                                                )', [$language_code, $attributeFilter->webattribute_id, $attributeFilter->value . "%"]);
                } else if ($attributeFilter->operator == 'ends') {
                    $q = $q->whereRaw('exists (select 1 from WebAttribute_WebPart
                                                                join WebModelTranslations on (translatable_type = "webAttributeValue" and column_name = "attribute_value" and translatable_id = WebAttribute_WebPart.id)
                                                        where WebAttribute_WebPart.webpart_id = WebParts.id
                                                                and WebModelTranslations.`language` = ?
                                                                and WebAttribute_WebPart.webattribute_id = ?
                                                                and WebModelTranslations.translation like ?
                                                                and WebAttribute_WebPart.deleted_at = 0
                                                                and exists (select 1 from WebAttribute_WebFamily
                                                                                    where WebAttribute_WebFamily.webfamily_id = WebFamilies.id
                                                                                    and WebAttribute_WebFamily.webattribute_id = WebAttribute_WebPart.webattribute_id
                                                                                    and WebAttribute_WebFamily.deleted_at = 0
                                                                            )
                                                )', [$language_code, $attributeFilter->webattribute_id, '%' . $attributeFilter->value]);
                }
                $q = $q->when($attributeFilter->must_exist == 0, function ($q2) use ($attributeFilter, $language_code) {
                    $q2->orWhereRaw('not exists (select 1 from WebAttribute_WebPart
                                                                    join WebModelTranslations on (translatable_type = "webAttributeValue" and column_name = "attribute_value" and translatable_id = WebAttribute_WebPart.id) 
                                                            where WebAttribute_WebPart.webpart_id = WebParts.id
                                                                    and WebModelTranslations.`language` = ?
                                                                    and WebAttribute_WebPart.webattribute_id = ?
                                                                    and WebAttribute_WebPart.deleted_at = 0
                                                                    and exists (select 1 from WebAttribute_WebFamily
                                                                                        where WebAttribute_WebFamily.webfamily_id = WebFamilies.id
                                                                                        and WebAttribute_WebFamily.webattribute_id = WebAttribute_WebPart.webattribute_id
                                                                                        and WebAttribute_WebFamily.deleted_at = 0
                                                                                )
                                                    )', [$language_code, $attributeFilter->webattribute_id]);
                });
                return $q;
            });
        }
        return $query;
    }

    public function createZip($datafeedjob = null)
    {
        $dataFeed = $this;
//        $logName = $this->option('log-name');
//        if (strlen($logName) <= 0){
//            $logName = $dataFeed->name;
//        }
        $this->configureLogging('datafeeds', $dataFeed->name);
        $this->notice("Running Datafeed $dataFeed->name");

        $tempDir = storage_path('datafeed_temp'.DIRECTORY_SEPARATOR);
        if(!is_dir($tempDir)){
            mkdir($tempDir, 0777, true);
        }

        $feedTempDir = $tempDir . $dataFeed->name . DIRECTORY_SEPARATOR;
        if(!is_dir($feedTempDir)){
            mkdir($feedTempDir, 0777, true);
        }

        $it = new RecursiveDirectoryIterator(storage_path('datafeed_temp'.DIRECTORY_SEPARATOR.$dataFeed->name.DIRECTORY_SEPARATOR), RecursiveDirectoryIterator::SKIP_DOTS);
        $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
        foreach($files as $file) {
            if ($file->isFile() && preg_match('/\.gitignore$/', $file->getFileName()) === 0){
                $this->debug("deleting ".$file->getRealPath());
                if (unlink($file->getRealPath()) === false){
                    $this->error("could not delete ".$file->getRealPath());
                }
            }
        }



        $localFilesPath = $feedTempDir;
        if(!is_dir($localFilesPath)){
            mkdir($localFilesPath, 0777, true);
        }


        $dataQuery = $dataFeed->buildQuery();

        $headers = array();
        $dataFeedFields = $dataFeed->datafeedFields()->where('enabled', '=', '1')->with('transformations')->with('datafeedfield.bindingRules')->with('bindingvalues.datafeedfieldbindingrule')->orderBy('overrideOrder')->get();

        $priceChecks = array();

        $dataQuery->addSelect('WebParts.part_id');

        $priceCustomerExists = false;
        $priceListExists = false;
        $discountExists = false;
        foreach($dataFeedFields as $datafeedField){
            array_push($headers, $datafeedField->fieldNameWithId);
            if ($datafeedField->datafeedfield->tableName == 'price') {
                if ($datafeedField->datafeedfield->columnName === 'base' || $datafeedField->datafeedfield->columnName === 'currencyCodeCustomer' || $datafeedField->datafeedfield->columnName === 'lvdtCustomer'|| $datafeedField->datafeedfield->columnName === 'fvdtCustomer') {
                    $priceCustomerExists = true;
                } else if ($datafeedField->datafeedfield->columnName === 'list' || $datafeedField->datafeedfield->columnName === 'currencyCodeList' || $datafeedField->datafeedfield->columnName === 'lvdtList'|| $datafeedField->datafeedfield->columnName === 'fvdtList') {
                    $priceListExists = true;
                } else if ($datafeedField->datafeedfield->columnName === 'discount' || $datafeedField->datafeedfield->columnName === 'discountPercent'){
                    // Need base price for discounts
                    $priceCustomerExists = true;
                    $discountExists = true;
                }

                continue; // Price calculations are done farther down after the parts are fetched
            } else if (!str_starts_with($datafeedField->datafeedfield?->rawValue ?? "", '#')) {
                $dataQuery->addSelect($datafeedField->selectValue);
            }

        }

        // TODO add order by
//            $dataQuery->orderBy('Brands.brandName', 'ASC')->orderBy('Styles.styleDesc', 'ASC');


        $this->debug("Running query: [{$dataQuery->toSql()}]");
        $this->debug("Bindings are: [".print_r($dataQuery->getBindings(),true)."]");
        $webParts = $dataQuery->get()->toArray();

        //Convert everything to an array because working with objects is cumbersome here
        $webPartsArray = array();
        foreach($webParts as $webPart) {
            $webPartsArray[] = (array)$webPart;
        }
        $webParts = $webPartsArray;

        // Okay we have to inject pricing in all jank-like

        if ($priceCustomerExists || $priceListExists ) {
            $customerSegment = $this->customer->customerSegment;
            $dummyCustomer = $this->customer->getDummyCustomer();
            $dummyCustomerSegment = $dummyCustomer->customerSegment;
            $pricelistConnection = PriceListSchema::getActivePriceListSchema()->getPriceListConnection();
            $m3DiscountCodes = M3DiscountCode::all()->pluck('discount_type', 'discount_code')->toArray();
            $discountBucketRecords = OGDIPO::on($pricelistConnection)->byDiscModel($customerSegment->getDiscountModel())->orderBy('DIPO', 'ASC')->get();

            $objectMappingRecord = CROBJC::on($pricelistConnection)->byPriceListTable($customerSegment->getPriceListTable())->first();
        }
        $count = 0;
        $count = 0;
        $max = count($webParts);

        if ($datafeedjob != null) {
            $datafeedjob->records_processed = 0;
            $datafeedjob->records_found = $max;
            $datafeedjob->save();    
        }

        foreach($webParts as $key => $webPart) {

            $count++;
            echo "$count : $max\r";
            $this->debug("$count : $max");

            $part = Part::find($webPart['part_id']);
            if ($priceCustomerExists) {
                $price = PriceHelper::getPriceRecord($pricelistConnection,  $this->customer, $part, 1, $customerSegment, $objectMappingRecord);
                if ($discountExists) {
                    if ($price == null) {
                        $discount = null;
                    } else {
                        $discount = PriceHelper::calculateDiscountDataForItem($pricelistConnection, $this->customer, $part, 1, $price->getSalePrice(), $m3DiscountCodes, $customerSegment, $discountBucketRecords);
                    }

                }
            };
            if ($priceListExists) {
                $listprice = PriceHelper::getPriceRecord($pricelistConnection,  $dummyCustomer, $part, 1, $dummyCustomerSegment, $objectMappingRecord);
            }

            //TODO localize with everything else

            foreach ($dataFeedFields as $datafeedField) {

                if ($datafeedField->datafeedfield->tableName == 'static') {
                    $webPart[$datafeedField->fieldNameWithId] = $datafeedField->static_text;
                }
                else if ($datafeedField->datafeedfield->tableName == 'price') {
                    if ($datafeedField->datafeedfield->columnName == 'base') {
                        if ($price != null) {
                            $webPart[$datafeedField->fieldNameWithId] = $datafeedField->currencyFormat($price->getSalePrice(), $price->CUCD);
                        } else {
                            $webPart[$datafeedField->fieldNameWithId] = "";
                        }
                    } else if ($datafeedField->datafeedfield->columnName == 'list') {
                        if ($listprice != null) {
                            $webPart[$datafeedField->fieldNameWithId] = $datafeedField->currencyFormat($listprice->getSalePrice(), $listprice->CUCD);
                        } else {
                            $webPart[$datafeedField->fieldNameWithId] = "";
                        }

                    } else if ($datafeedField->datafeedfield->columnName == 'discount') {
                        if ($discount != null && $price != null) {
                            $webPart[$datafeedField->fieldNameWithId] = "1";
                            $totalDiscount = 0;
                            foreach($discount as $bucket => $valueArray) {
                                $dia = $valueArray['DIA'.$valueArray["bucketNo"]];
                                if ($dia == "") {
                                    $dia = 0;
                                }
                                $totalDiscount += $dia;
                            }

                            $webPart[$datafeedField->fieldNameWithId] = $datafeedField->currencyFormat($price->getSalePrice() - $totalDiscount, $price->CUCD);
                        } else {
                            $webPart[$datafeedField->fieldNameWithId] = "";
                        }

                    } else if ($datafeedField->datafeedfield->columnName == 'discountPercent') {
                        if ($discount != null && $price != null) {
                            $webPart[$datafeedField->fieldNameWithId] = "1";
                            $totalDiscount = 0;
                            foreach($discount as $bucket => $valueArray) {
                                $dia = $valueArray['DIA'.$valueArray["bucketNo"]];
                                if ($dia == "") {
                                    $dia = 0;
                                }
                                $totalDiscount += $dia;
                            }
                            $webPart[$datafeedField->fieldNameWithId] = $totalDiscount / $price->getSalePrice() * 100;
                        } else {
                            $webPart[$datafeedField->fieldNameWithId] = "";
                        }

                    } else if ($datafeedField->datafeedfield->columnName == 'currencyCodeList') {
                        if ($listprice != null) {
                            $webPart[$datafeedField->fieldNameWithId] = $listprice->CUCD;
                        } else {
                            $webPart[$datafeedField->fieldNameWithId] = "";
                        }
                    } else if ($datafeedField->datafeedfield->columnName == 'currencyCodeCustomer') {
                        if ($price != null) {
                            $webPart[$datafeedField->fieldNameWithId] = $price->CUCD;
                        } else {
                            $webPart[$datafeedField->fieldNameWithId] = "";
                        }
                    } else if ($datafeedField->datafeedfield->columnName == 'fvdtCustomer') {
                        if ($listprice != null) {
                            $webPart[$datafeedField->fieldNameWithId] = $datafeedField->dateFormat($price->FVDT);
                        } else {
                            $webPart[$datafeedField->fieldNameWithId] = "";
                        }
                    } else if ($datafeedField->datafeedfield->columnName == 'fvdtList') {
                        if ($listprice != null) {
                            $webPart[$datafeedField->fieldNameWithId] = $datafeedField->dateFormat($listprice->FVDT);
                        } else {
                            $webPart[$datafeedField->fieldNameWithId] = "";
                        }
                    } else if ($datafeedField->datafeedfield->columnName == 'lvdtCustomer') {
                        if ($listprice != null) {
                            $webPart[$datafeedField->fieldNameWithIdWithId] = $datafeedField->dateFormat($price->LVDT);
                        } else {
                            $webPart[$datafeedField->fieldNameWithIdWithId] = "";
                        }
                    } else if ($datafeedField->datafeedfield->columnName == 'lvdtList') {
                        if ($listprice != null) {
                            $webPart[$datafeedField->fieldNameWithIdWithId] = $datafeedField->dateFormat($listprice->LVDT);
                        } else {
                            $webPart[$datafeedField->fieldNameWithIdWithId] = "";
                        }
                    }

                }

                foreach($datafeedField->transformations as $transformation) {
                    $webPart[$datafeedField->fieldNameWithId] = $transformation->transform($webPart[$datafeedField->fieldNameWithId]);
                }
            }
            $webParts[$key] = $webPart;

            if ($datafeedjob != null) {
                $datafeedjob->records_processed = $count;
                $datafeedjob->save();
            }
        }

        if ($dataFeed->export_type == 'csv') {
            // Build the CSV
            $fileName = trim($dataFeed->filename) == '' ? $dataFeed->name : $dataFeed->filename;
            $filePath = $localFilesPath.$fileName.".csv";
            $fileData = '';
            $csvDelimiter = $dataFeed->csv_delimiter == null || $dataFeed->csv_delimiter == '' ? "," : $dataFeed->csv_delimiter;
            if ($dataFeed->csv_includeheader == 1) {
                $row = [];
                foreach ($headers as $columnName) {
                    $row[] = preg_replace('/(.*)__(\d+)/i', '${1}',$columnName);
                }
                $fileData.= "\"".implode("\"$csvDelimiter\"", $row)."\"\n";
            }

            foreach($webParts as $webPart) {
                $row = [];
                foreach ($headers as $columnName){
                    $row[] = str_replace('"', '\"', $webPart[$columnName]);
                }
                $fileData.= "\"".implode("\"$csvDelimiter\"", $row)."\"\n";
            }
            file_put_contents($filePath, $fileData);
        } else if ($dataFeed->export_type == 'xlsx') {
            // Build the XLSX
            $filePath = $localFilesPath.$dataFeed->name.".xlsx";
            $sheetName = trim($dataFeed->xlsx_sheetname) == '' ? 'data' : $dataFeed->xlsx_sheetname;
            $export = new ArrayExport($webParts, $sheetName, $headers);
            $export->resortArray();
            $export->removeIdFromHeaders();
            $fileName = trim($dataFeed->filename) == '' ? $dataFeed->name : $dataFeed->filename;
            $result = Excel::store($export, $dataFeed->name.DIRECTORY_SEPARATOR.$fileName.".xlsx", 'datafeed_temp');
        }
        

        $this->notice("Data written to $filePath");

        if(!is_dir(storage_path("datafeed_zips/"))){
            mkdir(storage_path("datafeed_zips/"), 0777, true);
        }

        $localZipPath = $this->getLocalZipPath();
        if (is_file($localZipPath)){
            @unlink($localZipPath);
        }

        $date = Carbon::now();

        $zip = new ZipArchive($localZipPath);
        if (TRUE === $ret = $zip->open($localZipPath, ZipArchive::CREATE)) {
            $it = new RecursiveDirectoryIterator($localFilesPath, RecursiveDirectoryIterator::SKIP_DOTS);
            $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);


            foreach($files as $file) {

                $splitByDirectory = explode(DIRECTORY_SEPARATOR, $file->getRealPath());
                $append = false;
                $delim = $relativePath = "";
                foreach ($splitByDirectory as $fileOrDirectory){
                    if ($append){
                        $relativePath .= $delim . $fileOrDirectory;
                        $delim = DIRECTORY_SEPARATOR;
                    }

                    if ($fileOrDirectory === $dataFeed->name){
                        $append = true;
                    }
                }

                $this->debug("adding {$file->getRealPath()} to zip as $relativePath");
                if ($file->isFile() && preg_match('/\.gitignore$/', $file->getFileName()) === 0){
                    $zip->addFile($file->getRealPath(), $relativePath);
                }
            }
            $zip->close();
            touch($localZipPath, $date->getTimestamp());
        }

        $it = new RecursiveDirectoryIterator($localFilesPath, RecursiveDirectoryIterator::SKIP_DOTS);
        $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
        foreach($files as $file) {
            if ($file->isFile() && preg_match('/\.gitignore$/', $file->getFileName()) === 0){
                $this->debug("deleting ".$file->getRealPath());
                if (unlink($file->getRealPath()) === false){
                    $this->error("could not delete ".$file->getRealPath());
                }
            }else if ($file->isDir()){
                if (rmdir($file->getRealPath()) === false){
                    $this->error("could not delete ".$file->getRealPath());
                }
            }

        }
        if(is_dir($localFilesPath)){
            rmdir($localFilesPath);
        }

        $this->notice("Begin uploading $dataFeed->name");
        $allGood = true;

        $awsConfig = config('aws.awsDatafeedBucket');
        $s3 = S3Client::factory([
            'key'     => $awsConfig['key'],
            'secret'  => $awsConfig['secret'],
            'region'  => $awsConfig['region'],
            'version' => 'latest',
        ]);
        $bucket = $awsConfig['bucket'];

        $result = $s3->putObject([
            'Bucket' => $bucket,
            'Key' => $this->id.'.zip',
            'SourceFile' => $localZipPath
        ]);

        $datafeedjob->version_id = $result['VersionId'];
        $datafeedjob->save();

        $this->notice("$dataFeed->name complete");

        return $datafeedjob;
    }

    public function getLocalZipPath() {
        return storage_path('datafeed_zips/').$this->name.".zip";
    }
    
    // All attributefilters that exist on the object that aren't in the provided list should be deleted
    public function pruneAttributeFilters($foundAttributeFilterUUIDs) {
        foreach ($this->webattributefilters as $webattributefilter) {
            if (!in_array($webattributefilter->uuid, $foundAttributeFilterUUIDs)) {
                $webattributefilter->delete();
                $webattributefilter->save();
            }
        }
    }

    // All transformations that exist on the object that aren't in the provided list should be deleted
    public function pruneTransformations($foundTransformationUUIDs) {
        foreach ($this->datafeedFields as $datafeedField) {
            foreach ($datafeedField->transformations as $transformation) {
                if (!in_array($transformation->uuid, $foundTransformationUUIDs)) {
                    $transformation->delete();
                    $transformation->save();
                }
            }
        }

    }

    // All fields that exist on the object that aren't in the provided list should be deleted
    public function pruneFields($foundFieldIds) {
        foreach ($this->datafeedFields as $datafeedField) {
            if (!in_array($datafeedField->id, $foundFieldIds)) {
                $datafeedField->delete();
                $datafeedField->save();
            }
        }
    }

    use HasSharedCustomLoggingTrait;

    public function hasAvailablePlant($plantCode) {
        foreach(explode(",",$this->available_plants) as $availablePlant) {
            if ($plantCode == $availablePlant) {
                return true;
            }
        }

        return false;
    }
    
    public function firstStartTime() {
        return  new Carbon($this->autorun_startdate.' '. $this->autorun_hour.':'.$this->autorun_minute);
    }
    
    public function lastStartTime() {
        return  new Carbon($this->autorun_enddate.' '. $this->autorun_hour.':'.$this->autorun_minute);
    }
    
    public function hasWeeklyWeekday($weekday) {
        $weekdaysHad = explode(',',$this->autorun_weekly_weekdays);

        foreach($weekdaysHad as $weekdayHad) {
            if ($weekdayHad == $weekday) {
                return true;
            }
        }
        return false;
    }

    public function isOverdue()
    {
        // Not overdue if autorun is disabled
        if ($this->autorun_enabled == 0) return false;

        $now = Carbon::now();
        // Not overdue if we are before the datafeed's start date
        if ($now < $this->firstStartTime()) return false;
        // Not overdue if we are after the datafeed's end date
        if ($now > $this->lastStartTime()) return false;

        $nextRun = $this->findNextRun();
        // Not overdue if next run doesn't exist
        if ($this->findNextRun() == null) return false;


        return ($this->findNextRun() < now());
    }

    public function findNextRun() {
        // If autorun is disabled, there is no next run
        if ($this->autorun_enabled == 0) return null;

        $now = Carbon::now();

        // If we're passed the last run time, then
        if ($now > $this->lastStartTime()) return false;

        $mostRecentRun = $this->latestUnfailedJob;
        if ($mostRecentRun != null) {
            $submit_time = new Carbon($mostRecentRun->submit_time);
        } else {
            // If never ran before, then we might as well as pretend like it last ran just before the first time it's supposed to run
            $submit_time = new Carbon($this->firstStartTime());
            $submit_time->addDay(-1);
        }

        if ($this->autorun_cadence == 'daily') {
            $date = $this->firstStartTime();
            for ($date; $date <= $this->lastStartTime(); $date = $date->addDay()) {
                if ($date > $submit_time) {
                    return $date;
                }
            }
        } else if ($this->autorun_cadence == 'weekly') {
            $date = $this->firstStartTime();
            for ($date; $date <= $this->lastStartTime(); $date = $date->addWeek($this->autorun_weekly_frequency)->startOfWeek(Carbon::MONDAY)) {
                $checkdate = $date->copy();
                // Start of week wipes out the hour and minute, so put those back in for the date we care about
                $checkdate->hour($this->autorun_hour);
                $checkdate->minute($this->autorun_minute);

                // Progress through the whole week (Starting on Monday) checking appropriate days
                foreach(array(CARBON::MONDAY, CARBON::TUESDAY, CARBON::WEDNESDAY, CARBON::THURSDAY, CARBON::FRIDAY, CARBON::SATURDAY, CARBON::SUNDAY) as $dayOfWeek) {
                    // The only reason this if check is necessary is because the outer loop might not start on a Monday
                    if ($checkdate->dayOfWeek == $dayOfWeek) {
                        if ($this->hasWeeklyWeekday($dayOfWeek)) {
                            // If this day is greater than our submit time, this is the next time we are supposed to run
                            if ($checkdate > $submit_time) {
                                return $checkdate;
                            }
                        }
                        //increment the date so that we check the remaining days of the week
                        $checkdate = $checkdate->addDay(1);
                    }
                }
            }
            // If we made it this far, there are no more valid runs before end_date is reached
            return null;
        } else if($this->autorun_cadence == 'monthly') {

        } else if($this->autorun_cadence == 'yearly') {

        }
        return null;
    }


}
