/*
 * Copyright 2021-2024 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package product

import (
	"strings"
)

type Linter struct {
	Name            string
	PresentableName string
	ProductCode     string
	DockerImage     string
	SupportNative   bool
	IsPaid          bool
	SupportFixes    bool
	EapOnly         bool
}

const (
	ReleaseVersion = "2025.3"
	ShortVersion   = "253"
	IsReleased     = false

	EapSuffix  = "-EAP"
	ReleaseVer = "release"
	EapVer     = "eap"

	QDJVMC = "QDJVMC"
	QDJVM  = "QDJVM"
	QDAND  = "QDAND"
	QDPHP  = "QDPHP"
	QDPY   = "QDPY"
	QDPYC  = "QDPYC"
	QDJS   = "QDJS"
	QDGO   = "QDGO"
	QDNET  = "QDNET"
	QDNETC = "QDNETC"
	QDANDC = "QDANDC"
	QDRST  = "QDRST"
	QDRUBY = "QDRUBY"
	QDCLC  = "QDCLC"
	QDCPP  = "QDCPP"
)

var (
	UnknownLinter = Linter{}

	JvmLinter = Linter{
		PresentableName: "Qodana Ultimate for JVM",
		Name:            "qodana-jvm",
		ProductCode:     QDJVM,
		DockerImage:     "jetbrains/qodana-jvm",
		SupportNative:   true,
		IsPaid:          true,
		SupportFixes:    true,
		EapOnly:         false,
	}

	JvmCommunityLinter = Linter{
		PresentableName: "Qodana Community for JVM",
		Name:            "qodana-jvm-community",
		ProductCode:     QDJVMC,
		DockerImage:     "jetbrains/qodana-jvm-community",
		SupportNative:   true,
		IsPaid:          false,
		SupportFixes:    false,
		EapOnly:         false,
	}

	AndroidLinter = Linter{
		PresentableName: "Qodana for Android",
		Name:            "qodana-android",
		ProductCode:     QDAND,
		DockerImage:     "jetbrains/qodana-android",
		SupportNative:   false,
		IsPaid:          true,
		SupportFixes:    true,
		EapOnly:         false,
	}

	AndroidCommunityLinter = Linter{
		PresentableName: "Qodana Community for Android",
		Name:            "qodana-jvm-android",
		ProductCode:     QDANDC,
		DockerImage:     "jetbrains/qodana-jvm-android",
		SupportNative:   false,
		IsPaid:          false,
		SupportFixes:    false,
		EapOnly:         false,
	}

	PhpLinter = Linter{
		PresentableName: "Qodana for PHP",
		Name:            "qodana-php",
		ProductCode:     QDPHP,
		DockerImage:     "jetbrains/qodana-php",
		SupportNative:   true,
		IsPaid:          true,
		SupportFixes:    true,
		EapOnly:         false,
	}

	PythonLinter = Linter{
		PresentableName: "Qodana for Python",
		Name:            "qodana-python",
		ProductCode:     QDPY,
		DockerImage:     "jetbrains/qodana-python",
		SupportNative:   true,
		IsPaid:          true,
		SupportFixes:    true,
		EapOnly:         false,
	}

	PythonCommunityLinter = Linter{
		PresentableName: "Qodana Community for Python",
		Name:            "qodana-python-community",
		ProductCode:     QDPYC,
		DockerImage:     "jetbrains/qodana-python-community",
		SupportNative:   true,
		IsPaid:          false,
		SupportFixes:    false,
		EapOnly:         false,
	}

	JsLinter = Linter{
		PresentableName: "Qodana for JS",
		Name:            "qodana-js",
		ProductCode:     QDJS,
		DockerImage:     "jetbrains/qodana-js",
		SupportNative:   true,
		IsPaid:          true,
		SupportFixes:    true,
		EapOnly:         false,
	}

	DotNetLinter = Linter{
		PresentableName: "Qodana for .NET",
		Name:            "qodana-dotnet",
		ProductCode:     QDNET,
		DockerImage:     "jetbrains/qodana-dotnet",
		SupportNative:   true,
		IsPaid:          true,
		SupportFixes:    true,
		EapOnly:         false,
	}

	RubyLinter = Linter{
		PresentableName: "Qodana for Ruby",
		Name:            "qodana-ruby",
		ProductCode:     QDRUBY,
		DockerImage:     "jetbrains/qodana-ruby",
		SupportNative:   false,
		IsPaid:          true,
		SupportFixes:    true,
		EapOnly:         true,
	}

	CppLinter = Linter{
		PresentableName: "Qodana for C/C++",
		Name:            "qodana-cpp",
		ProductCode:     QDCPP,
		DockerImage:     "jetbrains/qodana-cpp",
		SupportNative:   true,
		IsPaid:          true,
		SupportFixes:    false,
		EapOnly:         true,
	}

	GoLinter = Linter{
		PresentableName: "Qodana for Go",
		Name:            "qodana-go",
		ProductCode:     QDGO,
		DockerImage:     "jetbrains/qodana-go",
		SupportNative:   true,
		IsPaid:          true,
		SupportFixes:    true,
		EapOnly:         false,
	}

	RustLinter = Linter{
		PresentableName: "Qodana for Rust",
		Name:            "qodana-rust",
		ProductCode:     QDRST,
		DockerImage:     "jetbrains/qodana-rust",
		SupportNative:   false,
		IsPaid:          true,
		SupportFixes:    false,
		EapOnly:         true,
	}

	DotNetCommunityLinter = Linter{
		PresentableName: "Qodana Community for .NET",
		Name:            "qodana-cdnet",
		ProductCode:     QDNETC,
		DockerImage:     "jetbrains/qodana-cdnet",
		SupportNative:   false,
		IsPaid:          false,
		SupportFixes:    false,
		EapOnly:         true,
	}

	ClangLinter = Linter{
		PresentableName: "Qodana Community for C/C++",
		Name:            "qodana-clang",
		ProductCode:     QDCLC,
		DockerImage:     "jetbrains/qodana-clang",
		SupportNative:   false,
		IsPaid:          false,
		SupportFixes:    false,
		EapOnly:         true,
	}

	// AllLinters Order is important for detection
	AllLinters = []Linter{
		JvmCommunityLinter,
		JvmLinter,
		AndroidCommunityLinter,
		AndroidLinter,
		PhpLinter,
		PythonCommunityLinter,
		PythonLinter,
		JsLinter,
		DotNetCommunityLinter,
		DotNetLinter,
		RubyLinter,
		CppLinter,
		GoLinter,
		RustLinter,
		ClangLinter,
	}
)

func (linter *Linter) NativeAnalyzer() Analyzer {
	return &NativeAnalyzer{
		Linter: *linter,
		Eap:    linter.EapOnly,
	}
}

func (linter *Linter) DockerAnalyzer() Analyzer {
	return &DockerAnalyzer{
		Linter: *linter,
		Image:  linter.Image(),
	}
}

func (linter *Linter) Image() string {
	//goland:noinspection GoBoolExpressions
	if !IsReleased || linter.EapOnly {
		return linter.DockerImage + ":" + ReleaseVersion + "-eap"
	}
	return linter.DockerImage + ":" + ReleaseVersion
}

// LangsToLinters is a map of languages to linters.
var LangsToLinters = map[string][]Linter{
	"Java": {
		JvmLinter,
		JvmCommunityLinter,
		AndroidLinter,
		AndroidCommunityLinter,
	},
	"Kotlin": {
		JvmLinter,
		JvmCommunityLinter,
		AndroidLinter,
		AndroidCommunityLinter,
	},
	"PHP":               {PhpLinter},
	"Python":            {PythonLinter, PythonCommunityLinter},
	"JavaScript":        {JsLinter},
	"TypeScript":        {JsLinter},
	"Go":                {GoLinter},
	"C#":                {DotNetLinter, DotNetCommunityLinter},
	"F#":                {DotNetLinter},
	"Visual Basic .NET": {DotNetLinter, DotNetCommunityLinter},
	"C":                 {CppLinter, ClangLinter, DotNetLinter},
	"C++":               {CppLinter, ClangLinter, DotNetLinter},
	"Ruby":              {RubyLinter},
	"Rust":              {RustLinter},
}

var AllSupportedFreeLinters = allLintersFiltered(AllLinters, func(linter *Linter) bool { return !linter.IsPaid })
var AllNativeLinters = allLintersFiltered(AllLinters, func(linter *Linter) bool { return linter.SupportNative })

func allImages(linters []Linter) []string {
	var images []string
	for _, linter := range linters {
		images = append(images, linter.Image())
	}
	return images
}

func allProductCodes(linters []Linter) []string {
	var images []string
	for _, linter := range linters {
		images = append(images, linter.ProductCode)
	}
	return images
}

func allLintersFiltered(linters []Linter, filter func(linter *Linter) bool) []Linter {
	var filtered []Linter
	for i := range linters {
		if filter(&linters[i]) {
			filtered = append(filtered, linters[i])
		}
	}
	return filtered
}

// AllImages is a list of all supported linters.
var AllImages = allImages(AllLinters)
var AllNames = allNames(AllLinters)

func allNames(linters []Linter) []string {
	var names []string
	for _, linter := range linters {
		names = append(names, linter.Name)
	}
	return names
}

var AllNativeProductCodes = allProductCodes(AllNativeLinters)

func FindLinterByImage(image string) Linter {
	image = strings.TrimPrefix(image, "https://")
	//goland:noinspection HttpUrlsUsage
	image = strings.TrimPrefix(image, "http://")
	if strings.HasPrefix(image, "registry.jetbrains.team/p/sa/containers/") {
		image = strings.TrimPrefix(image, "registry.jetbrains.team/p/sa/containers/")
		image = "jetbrains/" + image
	}
	for _, linter := range AllLinters {
		if strings.HasPrefix(image, linter.DockerImage) {
			return linter
		}
	}
	return UnknownLinter
}

func FindLinterByProductCode(productCode string) Linter {
	productCode = strings.TrimSuffix(productCode, EapSuffix)
	for _, linter := range AllLinters {
		if productCode == linter.ProductCode {
			return linter
		}
	}
	return UnknownLinter
}

func FindLinterByName(name string) Linter {
	name = strings.TrimSuffix(name, EapSuffix)
	for _, linter := range AllLinters {
		if name == linter.Name {
			return linter
		}
	}
	return UnknownLinter
}
