//
//  AnalyticsGenerator.swift
//
//
//  Created by Loris Perret on 08/12/2023.
//

// CPD-OFF

import CoreVideo
import Foundation
import ToolCore

// Disabled cause it's a pain to handle in generated string
// swiftlint:disable type_body_length

enum AnalyticsGenerator {

    // MARK: - Write content

    static func writeExtensionFiles(
        sections: [AnalyticsCategory],
        target: String,
        tags: [String],
        isStatic: Bool,
        outputFile: String,
        visibility: ExtensionVisibility
    ) {
        // Get target type from enum
        let targetsString: [String] = target.components(separatedBy: " ")
        let targets = {
            var targets = [TrackerType]()
            TrackerType.allCases.forEach { enumTarget in
                if targetsString.contains(enumTarget.value) {
                    targets.append(enumTarget)
                }
            }
            return targets
        }()

        // Get extension content
        let extensionFileContent = getExtensionContent(
            targets: targets,
            sections: sections,
            tags: tags,
            isStatic: isStatic,
            visibility: visibility
        )

        // Write content
        let outputFilePathURL = URL(fileURLWithPath: outputFile)
        do {
            try extensionFileContent.write(to: outputFilePathURL, atomically: false, encoding: .utf8)
        } catch {
            let error = AnalyticsError.writeFile(outputFile, error.localizedDescription)
            print(error.description)
            Analytics.exit(withError: error)
        }
    }

    // MARK: - Extension content

    static func getExtensionContent(
        targets: [TrackerType],
        sections: [AnalyticsCategory],
        tags: [String],
        isStatic: Bool,
        visibility: ExtensionVisibility
    ) -> String {
        [
            getHeader(
                targets: targets,
                isStatic: isStatic,
                visibility: visibility
            ),
            getProperties(
                sections: sections,
                tags: tags,
                isStatic: isStatic,
                visibility: visibility
            ),
            getFooter()
        ]
            .joined(separator: "\n")
    }

    // MARK: - Extension part

    private static func getHeader(
        targets: [TrackerType],
        isStatic: Bool,
        visibility: ExtensionVisibility
    ) -> String {
        """
        // Generated by ResgenSwift.\(Analytics.toolName) \(ResgenSwiftVersion)

        \(Self.getImport(targets: targets))

        \(Self.getAnalyticsProtocol(targets: targets, visibility: visibility))

        \(Self.getTrackerTypeEnum(targets: targets, visibility: visibility))

        // MARK: - Manager

        \(visibility) class AnalyticsManager {

            \(visibility) static var shared = AnalyticsManager()

            private init() {}

            // MARK: - Properties

            var managers: [TrackerType: AnalyticsManagerProtocol] = [:]

            \(Self.getEnabledContent(visibility: visibility))

            \(Self.getAnalyticsProperties(targets: targets, visibility: visibility))

            \(Self.getPrivateLogFunction())
        """
    }

    private static func getTrackerTypeEnum(
        targets: [TrackerType],
        visibility: ExtensionVisibility
    ) -> String {
        var result: [String] = []
        targets.forEach { type in
            result.append("    case \(type)")
        }

        return """
        // MARK: - Traker Type

        \(visibility) enum TrackerType: CaseIterable {

        \(result.joined(separator: "\n"))
        }
        """
    }

    private static func getEnabledContent(
        visibility: ExtensionVisibility
    ) -> String {
        """
        private var isDebugMode: Bool = false
        
            private var isEnabled: Bool {
                if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
                    false
                } else {
                    true
                }
            }
        
            private let logger = Logger(subsystem: "resgen", category: "analytics")

            // MARK: - Enable Methods

            private func setAnalytics(enable: Bool, _ analytics: [TrackerType]) {
                managers.forEach { (key, value) in
                    if analytics.contains(where: { type in
                        type == key
                    }) {
                        value.setEnable(enable)
                    }
                }
            }

            \(visibility) func enableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
                setAnalytics(enable: true, analytics)
            }

            \(visibility) func disableAnalytics(_ analytics: [TrackerType] = TrackerType.allCases) {
                setAnalytics(enable: false, analytics)
            }
        """
    }

    private static func getImport(targets: [TrackerType]) -> String {
        var result: [String] = []

        result.append("import Foundation")

        if targets.contains(TrackerType.matomo) {
            result.append("import MatomoTracker")
        }

        if targets.contains(TrackerType.firebase) {
            result.append("import FirebaseAnalytics")
        }

        result.append("import os")

        return result.joined(separator: "\n")
    }

    private static func getPrivateLogFunction() -> String {
    """
    // MARK: - Private Log Methods
    
        private func formattedParams(_ params: [String: Any]?) -> String {
            guard let params = params, !params.isEmpty else { return "-" }
            
            let formattedParams = params.map { key, value in
                "       \\(key): \\(value)"
            }
            .joined(separator: "\\n")
        
            return "\\n" + formattedParams
        }
    
        private func logScreen(
            name: String,
            path: String,
            params: [String: Any]?
        ) {
            guard isEnabled else {
                if isDebugMode {
                    logger.log("Analytics disabled")
                }
                return 
            }
    
            managers.values.forEach { manager in
                
                if isDebugMode {
                    logger.debug(\"""
                    🖥️ Screen:
                         Name: \\(name, privacy: .public)
                         Path: \\(path, privacy: .public)
                         Params: \\(self.formattedParams(params), privacy: .public)
                    \""")
                }
    
                manager.logScreen(
                    name: name,
                    path: path,
                    params: params
                )
            }
        }
    
        private func logEvent(
            name: String,
            action: String,
            category: String,
            params: [String: Any]?
        ) {
            guard isEnabled else {
                if isDebugMode {
                    logger.log("Analytics disabled")
                }
                return 
            }
    
            managers.values.forEach { manager in
                    
                if isDebugMode {
                    logger.debug(\"""
                    📊 Event:
                         Name: \\(name, privacy: .public)
                         Action: \\(action.isEmpty ? "-" : action, privacy: .public)
                         Category: \\(category.isEmpty ? "-" : category, privacy: .public)
                         Params: \\(self.formattedParams(params), privacy: .public)
                    \""")
                }
    
                manager.logEvent(
                    name: name,
                    action: action,
                    category: category,
                    params: params
                )
            }
        }
    """
    }

    private static func getAnalyticsProperties(
        targets: [TrackerType],
        visibility: ExtensionVisibility
    ) -> String {
        var header = ""
        var content: [String] = []
        let footer = "    }"

        if targets.contains(TrackerType.matomo) {
            header = "\(visibility) func configure(siteId: String, url: String, isDebugMode: Bool = false) {"
        } else if targets.contains(TrackerType.firebase) {
            header = "\(visibility) func configure(isDebugMode: Bool = false) {"
        }

        content.append("        self.isDebugMode = isDebugMode")

        if targets.contains(TrackerType.matomo) {
            content.append("""
                    managers[TrackerType.matomo] = MatomoAnalyticsManager(
                        siteId: siteId,
                        url: url
                    )
            """)
        }

        if targets.contains(TrackerType.firebase) {
            content.append("        managers[TrackerType.firebase] = FirebaseAnalyticsManager()")
        }

        return [
            header,
            content.joined(separator: "\n"),
            footer
        ]
            .joined(separator: "\n")
    }

    private static func getAnalyticsProtocol(
        targets: [TrackerType],
        visibility: ExtensionVisibility
    ) -> String {
        let proto = """
        // MARK: - Protocol

        \(visibility) protocol AnalyticsManagerProtocol {

            func logScreen(
                name: String,
                path: String,
                params: [String: Any]?
            )

            func logEvent(
                name: String,
                action: String,
                category: String,
                params: [String: Any]?
            )

            func setEnable(_ enable: Bool)
        }
        """

        var result: [String] = [proto]

        if targets.contains(TrackerType.matomo) {
            result.append(MatomoGenerator.service)
        }

        if targets.contains(TrackerType.firebase) {
            result.append(FirebaseGenerator.service)
        }

        return result.joined(separator: "\n\n")
    }

    private static func getProperties(
        sections: [AnalyticsCategory],
        tags: [String],
        isStatic: Bool,
        visibility: ExtensionVisibility
    ) -> String {
        sections
            .compactMap { section in
                // Check that at least one string will be generated
                guard section.hasOneOrMoreMatchingTags(tags: tags) else {
                    return nil// Go to next section
                }

                var res = "\n    // MARK: - \(section.id)"
                section.definitions.forEach { definition in
                    guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else {
                        return // Go to next definition
                    }

                    if isStatic {
                        res += "\n\n\(definition.getStaticProperty(visibility: visibility))"
                    } else {
                        res += "\n\n\(definition.getProperty(visibility: visibility))"
                    }
                }
                return res
            }
            .joined(separator: "\n")
    }

    private static func getFooter() -> String {
        """
        }

        """
    }
}

// CPD-ON
