require "csv"
require "faraday"
require "kconv"
require "banks_api/shinsei/js_parser"
require "banks_api/shinsei/bank_account"
require "banks_api/shinsei/transaction"
require "banks_api/shinsei/faraday_middleware"

module BanksApi
  module Shinsei
    class Session
      def initialize(credentials)
        @credentials = CredentialSet.new(credentials)
      end

      def fetch_accounts
        login
        data = connection.post(nil, accounts_data).body

        data["fldAccountID"].map.with_index do |account_id, index|
          BankAccount.new(
            id: account_id,
            type: data["fldAccountType"][index],
            description: data["fldAccountDesc"][index],
            currency: data["fldCurrCcy"][index],
            balance: data["fldCurrBalance"][index],
            base_balance: data["fldBaseBalance"][index],
            session: self
          )
        end

        # logout
      end

      def fetch_transactions(account:, from:, to:)
        login
        fetch_accounts
        post_data = transactions_data(account: account, from: from, to: to, step: 1)
        connection.post(nil, post_data)
        post_data = transactions_data(account: account, from: from, to: to, step: 2)
        csv = connection.post(nil, post_data).body.lines[9..-1].join
        headers = [:date, :ref_no, :description, :debit, :credit, :balance]
        CSV.parse(csv, col_sep: "\t", headers: headers).map do |csv_line|
          Transaction.from_csv_line(csv_line, currency: account.currency)
        end.reverse
      end

      private

        attr_reader :credentials, :session_id

        URL = "https://pdirect04.shinseibank.com/FLEXCUBEAt/LiveConnect.dll".freeze
        private_constant :URL

        def login
          data = connection.post(nil, login_phase1_data).body
          @session_id = data["fldSessionID"]
          connection.post(nil, login_phase2_data(data))
        end

        def logout
        end

        def connection
          @_connection ||= initialize_connection
        end

        def initialize_connection
          Faraday.new(url: URL) do |faraday|
            faraday.request(:url_encoded)
            faraday.use(FaradayMiddleware)
            faraday.adapter(Faraday.default_adapter)
          end
        end

        def login_phase1_data
          {
            "MfcISAPICommand" => "EntryFunc",
            "fldAppID" => "RT",
            "fldTxnID" => "LGN",
            "fldScrSeqNo" => "01",
            "fldRequestorID" => "41",
            "fldDeviceID" => "01",
            "fldUserID" =>  credentials.account,
            "fldUserNumId" =>  credentials.pin,
            "fldUserPass" =>  credentials.password,
            "fldLangID" => "ENG", # or JPN
            "fldRegAuthFlag" => "A"
          }
        end

        def login_phase2_data(data)
          {
            "MfcISAPICommand" => "EntryFunc",
            "fldAppID" => "RT",
            "fldTxnID" => "LGN",
            "fldScrSeqNo" => "41",
            "fldRequestorID" => "55",
            "fldSessionID" => session_id,
            "fldDeviceID" => "01",
            "fldGridChallange1" => credentials.get_grid_value(data["fldGridChallange1"]),
            "fldGridChallange2" => credentials.get_grid_value(data["fldGridChallange2"]),
            "fldGridChallange3" => credentials.get_grid_value(data["fldGridChallange3"]),
            "fldUserID" => "",
            "fldUserNumId" => "",
            "fldNumSeq" => "1",
            "fldLangID" => "ENG",
            "fldRegAuthFlag" => data["fldRegAuthFlag"]
          }
        end

        def accounts_data
          {
            "MfcISAPICommand" => "EntryFunc",
            "fldAppID" => "RT",
            "fldTxnID" => "ACS",
            "fldScrSeqNo" => "00",
            "fldRequestorID" => "23",
            "fldSessionID" => session_id,
            "fldAcctID" => "",
            "fldAcctType" => "CHECKING",
            "fldIncludeBal" => "Y",
            "fldPeriod" => "",
            "fldCurDef" => "JPY"
          }
        end

        def transactions_data(account:, from:, to:, step:)
          {
            "MfcISAPICommand" => "EntryFunc",
            "fldScrSeqNo" => "01",
            "fldAppID" => "RT",
            'fldSessionID'=> session_id,
            "fldTxnID" => %w(ACA DAA)[step-1],
            "fldRequestorID" => "9",
            "fldAcctID" => account.id,
            "fldAcctType" => account.type,
            "fldIncludeBal" => "N",
            "fldStartDate" => from.strftime("%Y%m%d"),
            "fldEndDate" => to.strftime("%Y%m%d"),
            "fldStartNum" => "0",
            "fldEndNum" => "0",
            "fldCurDef" => "JPY",
            "fldPeriod" => (from ? "2" : "1")
          }
        end
    end
  end
end
