//
//  lkfcTests.swift
//  lkfcTests
//
//  Created by Alexander Holzer on 06.06.18.
//  Copyright © 2018 Alexander Holzer. All rights reserved.
//

import XCTest
@testable import lkfc

class lkfcTests: XCTestCase {
    
    static let inPath = URL(fileURLWithPath: "\(LKFTest.rootPath)in", isDirectory: true)
    static let outPath = URL(fileURLWithPath: "\(LKFTest.rootPath)out", isDirectory: true)
    static let inSpecPath = URL(fileURLWithPath: "\(LKFTest.rootPath)inspec", isDirectory: true)
    static let packageInPath = URL(fileURLWithPath: "\(LKFTest.rootPath)packIn", isDirectory: true)
    static let packageOutPath = URL(fileURLWithPath: "\(LKFTest.rootPath)packOut", isDirectory: true)
    static let comparePath = URL(fileURLWithPath: "\(LKFTest.rootPath)compare", isDirectory: true)
    
    static func createContextWith (model: UInt32, flags : Int32 = 0, lines : [String]) throws  -> LKFContext {
        let context = try LKFContext(model: model, path: LKFTest.rootPath)
        context.flags = flags
        do {
            for s in lines {
                try context.addLine(line: s)
            }
        }
        catch {
            print (error.localizedDescription)
        }
        return context
    }
    
    static func createContextWith (model: UInt32, year: Int32, flags : Int32 = 0, lines : [String]) throws -> LKFContext {
        let context = try LKFContext(model: model, path: LKFTest.rootPath)
        if flags > 0 {
            context.flags = flags
        }
        context.setContextParameters(year: year)
        do {
            for s in lines {
                try context.addLine(line: s)
            }
        }
        catch {
            print (error.localizedDescription)
        }
        return context
    }
    
    static func createContextWith (provider: LKFContextStammProvider, model: UInt32, year: Int32, lines: [String]) throws -> LKFContext {
        let context = try LKFContext(model: model, path: LKFTest.rootPath, stammProvider:provider)
        
        if context.lastError != nil {
            return context
        }
        
        do {
            for s in lines {
                try context.addLine(line: s)
            }
        }
        catch {
            print (error.localizedDescription)
        }
        return context
        
    }

    static func resultForContext (context: LKFContext) -> String {
        do {
            try context.execute()
        }
        catch {
            XCTAssert(false, "error executing case: \(error.localizedDescription)")
            return ""
        }
        return context.getResultLine(forIndex:0)
    }
    
    static func combine (_ first:[String], _ second:[String]) -> [String] {
        var combined = [String]()
        combined.append(contentsOf: first)
        combined.append(contentsOf: second)
        return combined
    }
    
    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }
    
    func testExample() throws {
        let path = LKFTest.rootPath
        var context = try LKFContext (model: 2023, path: path)
        XCTAssert(context.lastError == nil)
        context = try LKFContext (model: 2024, path: path)
        XCTAssert(context.lastError == nil)
        context = try LKFContext (model: 2025, path: path)
        XCTAssert(context.lastError == nil)
        context = try LKFContext (model: 2026, path: path)
        XCTAssert(context.lastError == nil)
        do {
            context = try LKFContext (model: 2027, path: path)
            XCTFail("expected an exception")
        }
        catch {
            
        }
    }
    
    func testErrorList() throws {
        for model : UInt32 in 2023...2026 {
            do {
                let context = try LKFContext (model: model, path: LKFTest.rootPath)
                context.outputFormat = .utf8
                let outputErrorlistFile = LKFOutputErrorlistFile(context:context)
                let outputRowDescriptionFile = LKFOutputRowDescriptionFile(context: context)
                
                var file = lkfcTests.outPath.appendingPathComponent(outputErrorlistFile.defaultfileName, isDirectory: false)
                do {
                    try outputErrorlistFile.write(file: file)
                }
                catch {
                    XCTAssert(false, "could not create file \(file.absoluteString): \(error.localizedDescription)")
                }
                
                
                let fileCompare = LKFTestFileCompare (file: outputErrorlistFile, comparePath: lkfcTests.comparePath)
                if let differences  = try fileCompare.compare() {
                    for d in differences {
                        print (d.message)
                        print ("\n")
                    }
                    XCTAssert(false, "\(differences.count) differences in Error List")
                }
                
                file = lkfcTests.outPath.appendingPathComponent(outputRowDescriptionFile.defaultfileName, isDirectory: false)
                do {
                    try outputRowDescriptionFile.write(file: file)
                }
                catch {
                    XCTAssert(false, "could not create file \(file.absoluteString)")
                }
                
                let rowDescCompare = LKFTestFileCompare(file: outputRowDescriptionFile, comparePath:lkfcTests.comparePath)
                if let differences = try rowDescCompare.compare() {
                    for d in differences {
                        print (d.message)
                        print ("\n")
                    }
                    XCTAssert(false, "\(differences.count) differences in Satzart-Description)")
                }
            }
            catch {
                XCTAssert(false, error.localizedDescription)
            }
        }
    }
    
    func checkAufenthaltAbtMelData (aufenthalt: LKFAufenthalt, index: UInt32, trace:Bool) {
        
        guard aufenthalt.getField(fieldNum: 1) < "4" else {
            return
        }

        
        let result = aufenthalt.getResult()

        if let fv = result.getFieldValue(fieldIndex: 21), !fv.hasPrefix("HDG") {
            let countMEL = aufenthalt.melCount
            var mlp : Int = 0
            
            for j in 0..<countMEL {
                let scoringData = aufenthalt.melAt(index: j).scoringData()
                mlp += scoringData.leist
            }
            
            if trace || mlp != result.leistSum {
                
                print ("\(aufenthalt.key) : LP=\(result.leistSum), TP=\(result.tagSum), AMB=\(aufenthalt.isAmbulant) AMBTKL=\(aufenthalt.isAmbulantTKL) Index=\(index)")
                
                for j in 0..<countMEL {
                    let mel = aufenthalt.melAt(index: j)
                    let scoringData = mel.scoringData()
                    print ("   MEL \(mel.MEL) LP=\(scoringData.leist)")
                }
                
                XCTAssert (mlp == result.leistSum, "Aufenthalt \(aufenthalt.key): MEL-LP different (sum=\(mlp), lp=\(result.leistSum)")
            }
        }
        
        if !aufenthalt.isAmbulant {
            return
        }
        
        var lp : Double = 0
        var tp : Double = 0
        
        let countABT = aufenthalt.abtCount
        for j in 0..<countABT {
            let scoringData = aufenthalt.abtAt(index: j).scoringData()
            lp += scoringData.lp
            tp += scoringData.tp
        }
        
        lp = (lp * 100).rounded() / 100
        tp = (tp * 100).rounded() / 100
        
        if trace || lp != Double(result.leistSum) || tp != Double(result.tagSum) {
            
            print ("\(aufenthalt.key) : LP=\(result.leistSum), TP=\(result.tagSum), AMB=\(aufenthalt.isAmbulant) AMBTKL=\(aufenthalt.isAmbulantTKL) Index=\(index)")

            for j in 0..<countABT {
                let abt = aufenthalt.abtAt(index: j)
                let fields = abt.Fields
                let scoringData = abt.scoringData()
                print ("   ABT \(fields.0)/\(fields.1): LP=\(scoringData.lp) TP=\(scoringData.tp)")

            }
            
            XCTAssert(lp == Double(result.leistSum) && tp == Double(result.tagSum), "Aufenthalt \(aufenthalt.key): ABT-PT different (sumlp=\(lp), lp=\(result.leistSum) / sumtp=\(tp), tp=\(result.tagSum))")
        }

    }
    
    func checkAufenthaltAbtMelData (context: LKFContext) {
        
        let countAufenthalt = context.getAufenthaltCount()
        for i in 0..<countAufenthalt {
            let aufenthalt = context[i]
            checkAufenthaltAbtMelData(aufenthalt: aufenthalt, index: i, trace:false)
        }
    }

    static var fallSingleAbtMel = [
        "X01|K654|123||20190101|1053|I|19750301|40|W|AT|AT|1010|62376|||||||||15|J|0",
        "X02|K654|123||20190101|001|16111110|20190101|1053|J||||||N|40|2017013964",
        "X03|K654|123||20190101|G40.3|H|||2017013964",
        "X03|K654|123||20190101|G80.0|Z|||2017013964",
        "X04|K654|123||20190101|HF030||J|1|20190101|1053|16111110||2017013964",
        "X04|K654|123||20190101|QZ989||J|1|20190101|1053|16111110||2017013964",
        "X05|K654|123||20190101|HDG01.12|D|||139|857|||||996"
    ]
    
    func testSingleAbtMEL () throws {
        let context = try lkfcTests.createContextWith(model: 2020, year: 2019, lines: lkfcTests.fallSingleAbtMel)
        _ = lkfcTests.resultForContext(context: context)
        if context.getAufenthaltCount() > 0 {
            checkAufenthaltAbtMelData(aufenthalt: context[0], index: 0, trace: false)
        }
    }
    
    func testData() throws {
        let files : [URL]
        do {
            files = try FileManager.default.contentsOfDirectory(at: lkfcTests.inPath, includingPropertiesForKeys: nil, options: [])
        }
        catch {
            assert(false, "cannot read input files")
            return
        }
        
        let batchParameters = LKFBatchFileExecutionParametersForTest (outPath: lkfcTests.outPath)
        batchParameters.rootPath = LKFTest.rootPath
        batchParameters.createTestOutput = false
        batchParameters.comparePath = lkfcTests.comparePath
        batchParameters.keepContext = true

        var failCount = 0
        var lastFailure : String? = nil
        
        for inputFile in files {

            guard !inputFile.lastPathComponent.starts(with: ".") else {
                continue
            }
            
            print ("******* testdata: \(inputFile)")
            
            let execution = LKFBatchFileExecution(file: inputFile, parameters: batchParameters)
            let result = try execution.execute()
            
            if let failure = result as? LKFBatchFileExecutionResultFailed {
                lastFailure = failure.failureTextWithFile
                failCount += 1
            }
            
            if let context = execution.context {
                checkAufenthaltAbtMelData(context: context)
            }
        }
        
        if failCount > 0 {
            if failCount == 1 {
                XCTAssert(false, lastFailure!)
            }
            else {
                XCTAssert(false, "\(failCount) failed, last = \(lastFailure!)")
            }
        }
    }
    
    func testDataPackage() throws {
        let files : [URL]
        do {
            files = try FileManager.default.contentsOfDirectory(at: lkfcTests.packageInPath, includingPropertiesForKeys: nil, options: [])
        }
        catch {
            XCTAssert(false, "cannot read input files")
            return
        }
        
        let batchParameters = LKFBatchFileExecutionParametersForTest (outPath: lkfcTests.packageOutPath)
        batchParameters.rootPath = LKFTest.rootPath
        batchParameters.comparePath = lkfcTests.comparePath
        var failCount = 0
        var lastFailure : String? = nil
        
        for inputFile in files {
            
            let execution = LKFBatchFileExecution(file: inputFile, parameters: batchParameters)
            let result = try execution.execute(chunkSize:1000)
            
            if let failure = result as? LKFBatchFileExecutionResultFailed {
                lastFailure = failure.failureTextWithFile
                failCount += 1
            }
        }
        
        if failCount > 0 {
            if failCount == 1 {
                XCTAssert(false, lastFailure!)
            }
            else {
                XCTAssert(false, "\(failCount) failed, last = \(lastFailure!)")
            }
        }
    }

    func testDataPackageMulti() throws {
        let files : [URL]
        do {
            files = try FileManager.default.contentsOfDirectory(at: lkfcTests.packageInPath, includingPropertiesForKeys: nil, options: [])
        }
        catch {
            XCTAssert(false, "cannot read input files")
            return
        }
        
        let batchParameters = LKFBatchFileExecutionParametersForTest (outPath: lkfcTests.packageOutPath)
        batchParameters.rootPath = LKFTest.rootPath
        batchParameters.comparePath = lkfcTests.comparePath
        var failCount = 0
        var lastFailure : String? = nil
        
        for inputFile in files {
            
            let execution = LKFBatchFileExecution(file: inputFile, parameters: batchParameters)
            let result = try execution.execute(chunkSize:1000)
            
            if let failure = result as? LKFBatchFileExecutionResultFailed {
                lastFailure = failure.failureTextWithFile
                failCount += 1
            }
        }
        
        if failCount > 0 {
            if failCount == 1 {
                XCTAssert(false, lastFailure!)
            }
            else {
                XCTAssert(false, "\(failCount) failed, last = \(lastFailure!)")
            }
        }
    }

    
    func testC () {
        let test = LKFTest()
        if let sa = test.executeTestUtils() {
            for s in sa {
                print(s);
            }
            XCTAssert(false, "\(sa.count) issues")
        }
        if let sa = test.executeTestAufenthalt() {
            for s in sa {
                print(s);
            }
            XCTAssert(false, "\(sa.count) issues")
        }
    }
    
    static var case1 = [
    "X01|K303|123||20200101|0900|I|19640401|55|M|AT|AT|3323|30521|||||||||12|J|0||",
    "X02|K303|123||20200101|001|16221111|20200101|0900|J||||||N|55|123",
    "X02|K303|123||20200101|002|16225011|20200102|0158|J||||||N|55|123",
    "X03|K303|123||20200101|S89.9|H|||1920U20187",
    "X03|K303|123||20200101|929.9|Z|||1920U20187",
    "X04|K303|123||20200101|QZ050||J|0001|20200101|0958|16221111||123|MEL14.22|B",
    "X04|K303|123||20200101|ZZ532||J|0001|20200101|0958|16221111||123||",
    "X05|K303|123||20200101|MEL14.22|B|0|0|1106|702|0|0|0|0|"
    ]
    
    
    func testContact () throws {
        let context = try LKFContext(model: 2020, path: LKFTest.rootPath)
        context.setContextParameters(year: 2020)
        try context.addLines (lkfcTests.case1)
        try context.execute()
        try context.executeStat()

        _ = context.getErrors()
        let aufenthalt = context[0]
        for i in 0..<aufenthalt.melCount {
            let mel = aufenthalt.melAt(index: i)
            let scoringData = mel.scoringData()
            let grp = mel.getField(field: .mel)
            // print ("\(grp) \(scoringData.mel)")
        }
        
        // print (context.getResultLine(forIndex: 0))
    }

    func testStatContact () throws {
        let context = try LKFNoMetaDataContext(year: 2020, path: LKFTest.rootPath)
        context.setContextParameters(year: 2020)
        try context.addLines (lkfcTests.case1)
        try context.execute()
        try context.executeStat()
        
        let statFile = LKFOutputStatFile(context: context)
        let res = statFile.createOutput()
        
        /*
        for s in res {
            print("\(s)\n")
        }
        */
        _ = LKFStat(context: context)
        
    }
    
    func testStatOnlyFile() throws  {
        let files : [URL]
        do {
            files = try FileManager.default.contentsOfDirectory(at: lkfcTests.inPath, includingPropertiesForKeys: nil, options: [])
        }
        catch {
            XCTAssert(false, "cannot read input files")
            return
        }
        
        let batchParameters = LKFBatchFileExecutionParametersForTest (outPath: lkfcTests.outPath)
        batchParameters.rootPath = LKFTest.rootPath
        batchParameters.createTestOutput = false
        batchParameters.comparePath = lkfcTests.comparePath
        batchParameters.statOnly = true
        var failCount = 0
        var lastFailure : String? = nil
        
        for inputFile in files {
            
            guard !inputFile.lastPathComponent.starts(with: ".") else {
                continue
            }

            print ("******* testdata: \(inputFile)")
            
            let execution = LKFBatchFileExecution(file: inputFile, parameters: batchParameters)
            let result = try execution.execute()
            
            if let failure = result as? LKFBatchFileExecutionResultFailed {
                lastFailure = failure.failureTextWithFile
                failCount += 1
            }
        }
        
        if failCount > 0 {
            if failCount == 1 {
                XCTAssert(false, lastFailure!)
            }
            else {
                XCTAssert(false, "\(failCount) failed, last = \(lastFailure!)")
            }
        }
    }
}
