// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later

package repository

import (
	"context"
	"encoding/xml"

	repo_model "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/repo"
	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/git"
	"code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/log"
)

// manifestXML mirrors the .mokogitea/manifest.xml schema for XML parsing.
type manifestXML struct {
	XMLName      xml.Name             `xml:"mokoplatform"`
	Identity     manifestIdentity     `xml:"identity"`
	Governance   manifestGovernance   `xml:"governance"`
	Distribution manifestDistribution `xml:"distribution"`
	Build        manifestBuild        `xml:"build"`
}

type manifestDistribution struct {
	DisplayName   string `xml:"display-name"`
	Maintainer    string `xml:"maintainer"`
	MaintainerURL string `xml:"maintainer-url"`
	InfoURL       string `xml:"info-url"`
	TargetVersion string `xml:"target-version"`
	PHPMinimum    string `xml:"php-minimum"`
}

type manifestIdentity struct {
	Name          string          `xml:"name"`
	Org           string          `xml:"org"`
	Description   string          `xml:"description"`
	VersionPrefix string          `xml:"version-prefix"`
	ElementName   string          `xml:"element-name"`
	License       manifestLicense `xml:"license"`
}

type manifestLicense struct {
	SPDX string `xml:"spdx,attr"`
	Name string `xml:",chardata"`
}

type manifestGovernance struct {
	Platform         string `xml:"platform"`
	StandardsVersion string `xml:"standards-version"`
	StandardsSource  string `xml:"standards-source"`
}

type manifestBuild struct {
	Language    string `xml:"language"`
	ExtensionType string `xml:"package-type"`
	EntryPoint  string `xml:"entry-point"`
}

// SyncMetadataFromCommit reads .mokogitea/manifest.xml from the given commit
// and upserts the values into the repo_manifest database table.
// This is called on push to the default branch to keep the database in sync
// with the XML file. If no manifest.xml exists, this is a no-op.
func SyncMetadataFromCommit(ctx context.Context, repo *repo_model.Repository, commit *git.Commit) {
	if commit == nil {
		return
	}

	entry, err := commit.GetTreeEntryByPath(".mokogitea/manifest.xml")
	if err != nil || entry == nil {
		return // no manifest.xml — not an error
	}

	reader, err := entry.Blob().DataAsync()
	if err != nil {
		log.Error("SyncMetadata: read blob for %s: %v", repo.FullName(), err)
		return
	}
	defer reader.Close()

	var mxml manifestXML
	decoder := xml.NewDecoder(reader)
	if err := decoder.Decode(&mxml); err != nil {
		log.Error("SyncMetadata: parse XML for %s: %v", repo.FullName(), err)
		return
	}

	manifest := &repo_model.RepoMetadata{
		RepoID:           repo.ID,
		Name:             mxml.Identity.Name,
		Org:              mxml.Identity.Org,
		Description:      mxml.Identity.Description,
		VersionPrefix:    mxml.Identity.VersionPrefix,
		ElementName:      mxml.Identity.ElementName,
		LicenseSPDX:      mxml.Identity.License.SPDX,
		LicenseName:      mxml.Identity.License.Name,
		Platform:         mxml.Governance.Platform,
		StandardsVersion: mxml.Governance.StandardsVersion,
		StandardsSource:  mxml.Governance.StandardsSource,
		Maintainer:       mxml.Distribution.Maintainer,
		MaintainerURL:    mxml.Distribution.MaintainerURL,
		InfoURL:          mxml.Distribution.InfoURL,
		TargetVersion:    mxml.Distribution.TargetVersion,
		PHPMinimum:       mxml.Distribution.PHPMinimum,
		Language:         mxml.Build.Language,
		ExtensionType:      mxml.Build.ExtensionType,
		EntryPoint:       mxml.Build.EntryPoint,
	}

	if err := repo_model.CreateOrUpdateRepoMetadata(ctx, manifest); err != nil {
		log.Error("SyncMetadata: save for %s: %v", repo.FullName(), err)
		return
	}

	log.Info("SyncMetadata: synced .mokogitea/manifest.xml for %s", repo.FullName())
}
