//
//  AnalyticsFileParser.swift
//
//
//  Created by Loris Perret on 06/12/2023.
//

import Foundation
import Yams

class AnalyticsFileParser {
    private static var inputFile: String = ""
    private static var target: String = ""
    
    private static func parseYaml() -> AnalyticsFile {
        guard let data = FileManager().contents(atPath: inputFile) else {
            let error = AnalyticsError.fileNotExists(inputFile)
            print(error.description)
            Analytics.exit(withError: error)
        }
        
        do {
            let tagFile = try YAMLDecoder().decode(AnalyticsFile.self, from: data)
            return tagFile
        } catch {
            let error = AnalyticsError.parseFailed(error.localizedDescription)
            print(error.description)
            Analytics.exit(withError: error)
        }
    }

    private static func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
        parameters.map { dtoParameter in
            // Type
            
            let type = dtoParameter.type.uppercasedFirst()

            guard 
                type == "String" ||
                type == "Int" ||
                type == "Double" ||
                type == "Bool"
            else {
                let error = AnalyticsError.invalidParameter("type of \(dtoParameter.name)")
                print(error.description)
                Analytics.exit(withError: error)
            }

            let parameter = AnalyticsParameter(
                name: dtoParameter.name,
                type: type
            )

            if let replaceIn = dtoParameter.replaceIn {
                parameter.replaceIn = replaceIn.components(separatedBy: ",")
            }

            return parameter
        }
    }

    private static func getTagDefinition(
        id: String,
        name: String,
        type: AnalyticsDefinition.TagType,
        tags: String,
        comments: String?,
        parameters: [AnalyticsParameterDTO]?
    ) -> AnalyticsDefinition {
        let definition = AnalyticsDefinition(id: id, name: name, type: type)
        definition.tags = tags
            .components(separatedBy: ",")
            .map { $0.removeLeadingTrailingWhitespace() }

        if let comments = comments {
            definition.comments = comments
        }
        
        if let parameters = parameters {
            definition.parameters = Self.getParameters(from: parameters)
        }
        
        return definition
    }
    
    private static func getTagDefinitionScreen(from screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] {
        screens.map { screen in
            let definition: AnalyticsDefinition = Self.getTagDefinition(
                id: screen.id,
                name: screen.name,
                type: .screen,
                tags: screen.tags,
                comments: screen.comments,
                parameters: screen.parameters
            )

            if target.contains(TrackerType.matomo.value) {
                // Path

                guard let path = screen.path else {
                    let error = AnalyticsError.missingElement("screen path")
                    print(error.description)
                    Analytics.exit(withError: error)
                }

                definition.path = path
            }

            return definition
        }
    }
    
    private static func getTagDefinitionEvent(from events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] {
        events.map { event in
            let definition: AnalyticsDefinition = Self.getTagDefinition(
                id: event.id,
                name: event.name,
                type: .event,
                tags: event.tags,
                comments: event.comments,
                parameters: event.parameters
            )

            if target.contains(TrackerType.matomo.value) {
                // Category
                guard let category = event.category else {
                    let error = AnalyticsError.missingElement("event category")
                    print(error.description)
                    Analytics.exit(withError: error)
                }

                definition.category = category

                // Action
                guard let action = event.action else {
                    let error = AnalyticsError.missingElement("event action")
                    print(error.description)
                    Analytics.exit(withError: error)
                }

                definition.action = action
            }

            return definition
        }
    }
    
    static func parse(_ inputFile: String, target: String) -> [AnalyticsCategory] {
        self.inputFile = inputFile
        self.target = target
        
        let tagFile = Self.parseYaml()
        
        return tagFile
            .categories
            .map { categorie in
                let section: AnalyticsCategory = AnalyticsCategory(id: categorie.id)

                if let screens = categorie.screens {
                    section
                        .definitions
                        .append(
                            contentsOf: Self.getTagDefinitionScreen(from: screens)
                        )
                }

                if let events = categorie.events {
                    section
                        .definitions
                        .append(
                        contentsOf: Self.getTagDefinitionEvent(from: events)
                    )
                }

                return section
            }
    }
}
