package main

import (
	"encoding/csv"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/sagernet/sing-box/log"

	"golang.org/x/exp/slices"
)

func main() {
	err := updateMozillaIncludedRootCAs()
	if err != nil {
		log.Error(err)
	}
	err = updateChromeIncludedRootCAs()
	if err != nil {
		log.Error(err)
	}
}

func updateMozillaIncludedRootCAs() error {
	response, err := http.Get("https://ccadb.my.salesforce-sites.com/mozilla/IncludedCACertificateReportPEMCSV")
	if err != nil {
		return err
	}
	defer response.Body.Close()
	reader := csv.NewReader(response.Body)
	header, err := reader.Read()
	if err != nil {
		return err
	}
	geoIndex := slices.Index(header, "Geographic Focus")
	certIndex := slices.Index(header, "PEM Info")

	pemBundle := strings.Builder{}
	for {
		record, err := reader.Read()
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		if record[geoIndex] == "China" {
			continue
		}
		cert := record[certIndex]
		cert = cert[1 : len(cert)-1]
		pemBundle.WriteString(cert)
		pemBundle.WriteString("\n")
	}
	return writeGeneratedCertificateBundle("mozilla", "mozillaIncluded", pemBundle.String())
}

func fetchChinaFingerprints() (map[string]bool, error) {
	response, err := http.Get("https://ccadb.my.salesforce-sites.com/ccadb/AllCertificateRecordsCSVFormatv4")
	if err != nil {
		return nil, err
	}
	defer response.Body.Close()
	reader := csv.NewReader(response.Body)
	header, err := reader.Read()
	if err != nil {
		return nil, err
	}
	countryIndex := slices.Index(header, "Country")
	fingerprintIndex := slices.Index(header, "SHA-256 Fingerprint")

	chinaFingerprints := make(map[string]bool)
	for {
		record, err := reader.Read()
		if err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
		if record[countryIndex] == "China" {
			chinaFingerprints[record[fingerprintIndex]] = true
		}
	}
	return chinaFingerprints, nil
}

func updateChromeIncludedRootCAs() error {
	chinaFingerprints, err := fetchChinaFingerprints()
	if err != nil {
		return err
	}

	response, err := http.Get("https://ccadb.my.salesforce-sites.com/ccadb/RootCACertificatesIncludedByRSReportCSV")
	if err != nil {
		return err
	}
	defer response.Body.Close()
	reader := csv.NewReader(response.Body)
	header, err := reader.Read()
	if err != nil {
		return err
	}
	statusIndex := slices.Index(header, "Google Chrome Status")
	certIndex := slices.Index(header, "X.509 Certificate (PEM)")
	fingerprintIndex := slices.Index(header, "SHA-256 Fingerprint")

	pemBundle := strings.Builder{}
	for {
		record, err := reader.Read()
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		if record[statusIndex] != "Included" {
			continue
		}
		if chinaFingerprints[record[fingerprintIndex]] {
			continue
		}
		cert := record[certIndex]
		if len(cert) > 0 && cert[0] == '\'' {
			cert = cert[1 : len(cert)-1]
		}
		pemBundle.WriteString(cert)
		pemBundle.WriteString("\n")
	}
	return writeGeneratedCertificateBundle("chrome", "chromeIncluded", pemBundle.String())
}

func writeGeneratedCertificateBundle(name string, variableName string, pemBundle string) error {
	goSource := `// Code generated by 'make update_certificates'. DO NOT EDIT.

package certificate

import (
	"crypto/x509"
	_ "embed"
)

//go:embed ` + name + `.pem
var ` + variableName + `PEM string

var ` + variableName + ` *x509.CertPool

func init() {
	` + variableName + ` = x509.NewCertPool()
	` + variableName + `.AppendCertsFromPEM([]byte(` + variableName + `PEM))
}
`
	err := os.WriteFile(filepath.Join("common/certificate", name+".pem"), []byte(pemBundle), 0o644)
	if err != nil {
		return err
	}
	return os.WriteFile(filepath.Join("common/certificate", name+".go"), []byte(goSource), 0o644)
}
