using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Text;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;

using CSVDownloader.Code;
using CSVDownloader.Store.ElectronicMoneyDataStore;
using CSVDownloader.Store.CreditCSVData;
using CSVDownloader.Exceptions;

using ExpectedConditions = OpenQA.Selenium.Support.UI.ExpectedConditions;
using System.Globalization;

namespace CSVDownloader.Web {
    class GMOController : WebController {

        private readonly String url_ = "https://webinq.gmo-fg.net/KessaisyoukaiGUI/#no-back";


        private readonly String filename_zip = "gmo.zip";
        private readonly String filename_credit = "gmo_credit.csv";
        private readonly String filename_electronic_money = "gmo_electronic_money.csv";

        private enum XpathKey {
            INPUT_LOGIN_CUSTOMER_CODE,
            INPUT_LOGIN_USERNAME,
            INPUT_LOGIN_PASSWORD,

            BUTTON_LOGIN,

            FRAME_MAIN,


            LOADING,
            PROGRESS,

            INPUT_FROM_DATE,
            INPUT_TO_DATE,
            BUTTON_SEARCH,
            BUTTON_DOWNLOAD_ZIP,

            BUTTON_LOGOUT,
        };

        private IDictionary<XpathKey, string> xpath_map_ = new Dictionary<XpathKey, string>()
      {
             {XpathKey.INPUT_LOGIN_CUSTOMER_CODE,@"//*[@id='companyCode']"},
             {XpathKey.INPUT_LOGIN_USERNAME,@"//*[@id='userName']"},
             {XpathKey.INPUT_LOGIN_PASSWORD,@"//*[@id='password']"},

             {XpathKey.BUTTON_LOGIN,@"//*[@id='loginForm']//input[@type='submit']"},

             {XpathKey.FRAME_MAIN,@"//*[@id='main']"},



             {XpathKey.INPUT_FROM_DATE,@"//*[@id='_searchDateFrom']"},
             {XpathKey.INPUT_TO_DATE,@"//*[@id='_searchDateTo']"},


             {XpathKey.BUTTON_SEARCH,$"//*[@id='Content']//input[@value='検索']" },
             {XpathKey.BUTTON_DOWNLOAD_ZIP,$"//*[@id='Content']//input[@value='実売上取引出力']" },

             {XpathKey.BUTTON_LOGOUT,@"//a[@href='javascript:logout();']" },

             {XpathKey.LOADING,@"//*[@id='loadingFont']"},
             {XpathKey.PROGRESS,@"//*[@id='progress']"},

         };

        public GMOController(ChromeDriver driver) : base(driver) {
            agent_ = CreditAgent.HT;
        }

        public override ResultCode Login(LoginInfo info) {

            try {
                driver_.Navigate().GoToUrl(url_);
                Send(xpath_map_[XpathKey.INPUT_LOGIN_CUSTOMER_CODE], info.company_name);
                Send(xpath_map_[XpathKey.INPUT_LOGIN_USERNAME], info.user_name);
                Send(xpath_map_[XpathKey.INPUT_LOGIN_PASSWORD], info.password);

                Click(xpath_map_[XpathKey.BUTTON_LOGIN]);

                CloseAlert();

                // ログイン後の「端末決済データダウンロード」が表示されることを確認する。
                SwitchToMainFrame();
                wait_.Until(ExpectedConditions.ElementToBeClickable(By.XPath(xpath_map_[XpathKey.BUTTON_DOWNLOAD_ZIP])));
                SwitchToFrame();


            } catch (Exception e) {
                logger_.Error(e.Message);
                return ResultCode.NG;
            }
            return ResultCode.OK;
        }

        public override ResultCode Download(DateTime from, DateTime to) {


            SwitchToMainFrame();

            // 承認処理日のFrom-Toを入力
            Clear(xpath_map_[XpathKey.INPUT_FROM_DATE]);
            Clear(xpath_map_[XpathKey.INPUT_TO_DATE]);
            Send(xpath_map_[XpathKey.INPUT_FROM_DATE], from.ToString("yyyy/MM/dd"));
            Send(xpath_map_[XpathKey.INPUT_TO_DATE], to.ToString("yyyy/MM/dd"));


            // ダウンロード開始

            Click(xpath_map_[XpathKey.BUTTON_SEARCH]);
            wait_.Until(ExpectedConditions.InvisibilityOfElementLocated(By.XPath(xpath_map_[XpathKey.LOADING])));
            Click(xpath_map_[XpathKey.BUTTON_DOWNLOAD_ZIP]);
            CloseAlert();


            var long_wait = new WebDriverWait(driver_, TimeSpan.FromSeconds(360));
            long_wait.Until(ExpectedConditions.InvisibilityOfElementLocated(By.XPath(xpath_map_[XpathKey.PROGRESS])));


            WaitForDownload(filename_zip);



            // 解凍
            ArchiveExtract();

            SwitchToFrame();

            return ResultCode.OK;
        }

        public override ResultCode Logout() {

            try {
                Click(xpath_map_[XpathKey.BUTTON_LOGOUT]);

            } catch (Exception e) {
                logger_.Error(e.Message);
                return ResultCode.NG;
            }

            return ResultCode.OK;
        }


        public override List<CreditCSVData> GetCreditCSVDataList() {

            var result_list = new List<CreditCSVData>();


            var list = GetFileContents(filename_credit);

            bool failed_flg = false;
            var failed_parking_name_hs_table = new HashSet<String>();

            foreach (var line in list) {
                var parking_name = line[(int)CreditCSVDataGMO.ColName.ShopName];
                try {


                    int amount = 0;
                    int.TryParse(line[(int)CreditCSVDataGMO.ColName.Amount], out amount);

                    int tax_send_amount = 0;
                    int.TryParse(line[(int)CreditCSVDataGMO.ColName.TaxSendAmount], out tax_send_amount);

                    DateTime processing_date;
                    DateTime.TryParseExact(line[(int)CreditCSVDataGMO.ColName.ProcessingDate], "yyMMdd", null, DateTimeStyles.None, out processing_date);

                    result_list.Add(new CreditCSVDataGMO() {
                        spot_id = GetSpotID(parking_name),
                        customer_code = line[(int)CreditCSVDataGMO.ColName.CustomerCode],
                        output_datetime = line[(int)CreditCSVDataGMO.ColName.OutputDatetime],
                        processing_code = line[(int)CreditCSVDataGMO.ColName.ProcessingCode],
                        operation_code = line[(int)CreditCSVDataGMO.ColName.OperationCode],
                        return_code = line[(int)CreditCSVDataGMO.ColName.ReturnCode],
                        device_no = line[(int)CreditCSVDataGMO.ColName.DeviceNo],
                        target_company_code = line[(int)CreditCSVDataGMO.ColName.TargetCompanyCode],
                        target_company_sub_code = line[(int)CreditCSVDataGMO.ColName.TargetCompanySubCode],
                        device_trade_no = line[(int)CreditCSVDataGMO.ColName.DeviceTradeNo],
                        processing_date = processing_date,
                        approval_no = line[(int)CreditCSVDataGMO.ColName.ApprovalNo],
                        amount = amount,
                        tax_send_amount = tax_send_amount,
                        customer_name = line[(int)CreditCSVDataGMO.ColName.CustomerName],
                        shop_name = line[(int)CreditCSVDataGMO.ColName.ShopName],
                        cancel_receipt_no = line[(int)CreditCSVDataGMO.ColName.CancelReceiptNo],
                        split_count_info = line[(int)CreditCSVDataGMO.ColName.SplitCountInfo],
                        paying_type = line[(int)CreditCSVDataGMO.ColName.PayingType],
                    });

                } catch (ArgumentException) {

                    failed_parking_name_hs_table.Add(parking_name);
                    failed_flg = true;
                    continue;
                }
            }

            if (failed_flg) {
                var error_list = new List<Store.ParkingCreditCardAgenciesSpotIDError>();
                logger_.Error("失敗駐車場名");
                foreach (var ele in failed_parking_name_hs_table) {
                    error_list.Add(new Store.ParkingCreditCardAgenciesSpotIDError() {
                        creditcard_agencies_id = CreditAgenciesMap.GetID(agent_),
                        creditcard_agencies_spot_name = ele
                    });
                    logger_.Error($"駐車場 \"{ele}\"");
                }

                throw new SpotNameNotMatchException(error_list);
            }


            return result_list;
        }

        public override List<ElectronicMoneyCSVData> GetElectronicMoneyCSVDataList() {
            var result_list = new List<ElectronicMoneyCSVData>();


            var list = GetFileContents(filename_electronic_money);

            bool failed_flg = false;
            var failed_parking_name_hs_table = new HashSet<String>();

            foreach (var line in list) {
                var parking_name = line[(int)GMOElectronicMoneyData.ColName.ShopName];
                try {


                    int amount = 0;
                    int.TryParse(line[(int)GMOElectronicMoneyData.ColName.Amount], out amount);

                    int auto_charge_amount = 0;
                    int.TryParse(line[(int)GMOElectronicMoneyData.ColName.AutoChargeAmount], out auto_charge_amount);

                    int before_deposit = 0;
                    int.TryParse(line[(int)GMOElectronicMoneyData.ColName.BeforeDeposit], out before_deposit);

                    //DateTime adjust_datetime;
                    //DateTime.TryParseExact(line[(int)GMOElectronicMoneyData.ColName.AdjustDatetime], "yyMMdd", null, DateTimeStyles.None, out adjust_datetime);


                    result_list.Add(new GMOElectronicMoneyData() {
                        spot_id = GetSpotID(parking_name),
                        customer_code = line[(int)GMOElectronicMoneyData.ColName.CustomerCode],
                        acquirer_id = line[(int)GMOElectronicMoneyData.ColName.AcquirerID],
                        acquirer_name = line[(int)GMOElectronicMoneyData.ColName.AcquirerName],
                        customer_id = line[(int)GMOElectronicMoneyData.ColName.CustomerID],
                        customer_name = line[(int)GMOElectronicMoneyData.ColName.CustomerName],
                        shop_name = line[(int)GMOElectronicMoneyData.ColName.ShopName],
                        device_id = line[(int)GMOElectronicMoneyData.ColName.DeviceID],
                        host_device_id = line[(int)GMOElectronicMoneyData.ColName.HostDeviceID],
                        device_no = line[(int)GMOElectronicMoneyData.ColName.DeviceNo],
                        adjust_datetime = DateTime.Parse(line[(int)GMOElectronicMoneyData.ColName.AdjustDatetime]),
                        trade_code = line[(int)GMOElectronicMoneyData.ColName.TradeCode],
                        amount = amount,
                        auto_charge_amount = auto_charge_amount,
                        before_deposit = before_deposit,
                        brand = line[(int)GMOElectronicMoneyData.ColName.Brand],
                        card_id = line[(int)GMOElectronicMoneyData.ColName.CardID],
                        trade_status = line[(int)GMOElectronicMoneyData.ColName.TradeStatus],
                        last_proccesing_datetime = line[(int)GMOElectronicMoneyData.ColName.LastProccesingDatetime],
                        point = line[(int)GMOElectronicMoneyData.ColName.Point],
                        trade_no = line[(int)GMOElectronicMoneyData.ColName.TradeNo],
                        origin_trade_no = line[(int)GMOElectronicMoneyData.ColName.OriginTradeNo],

                    });

                } catch (ArgumentException) {

                    failed_parking_name_hs_table.Add(parking_name);
                    failed_flg = true;
                    continue;
                }
            }

            if (failed_flg) {
                var error_list = new List<Store.ParkingCreditCardAgenciesSpotIDError>();
                logger_.Error("失敗駐車場名");
                foreach (var ele in failed_parking_name_hs_table) {
                    error_list.Add(new Store.ParkingCreditCardAgenciesSpotIDError() {
                        creditcard_agencies_id = CreditAgenciesMap.GetID(agent_),
                        creditcard_agencies_spot_name = ele
                    });
                    logger_.Error($"駐車場 \"{ele}\"");
                }

                throw new SpotNameNotMatchException(error_list);
            }


            return result_list;
        }


        private List<String[]> GetFileContents(String filename) {
            var list = new List<String[]>();
            var dir = new DirectoryInfo(DriverFactory.GetDownloadDir());
            var config = new CSVConfig() {
                header = true,
            };
            config.remove_char.Add("\"");
            var sjis_enc = Encoding.GetEncoding("Shift-JIS");

            foreach (var file in dir.GetFiles()) {
                if (file.Name.Contains(filename)) {
                    var result = ReadCsv(file.FullName, config, sjis_enc);
                    list.AddRange(result);
                }
            }

            return list;
        }


        private void ArchiveExtract() {
            using (ZipArchive archive = ZipFile.OpenRead(Path.Combine(DriverFactory.GetDownloadDir(), filename_zip))) {
                foreach (ZipArchiveEntry entry in archive.Entries) {
                    if (entry.FullName.Contains("FEP")) {
                        entry.ExtractToFile(Path.Combine(DriverFactory.GetDownloadDir(), filename_credit));
                    }
                    if (entry.FullName.Contains("DEN")) {
                        entry.ExtractToFile(Path.Combine(DriverFactory.GetDownloadDir(), filename_electronic_money));
                    }
                }
            }

        }

        private void SwitchToMainFrame() {
            SwitchToFrame(xpath_map_[XpathKey.FRAME_MAIN]);
        }
    }
}
