// Copyright 2026 MarketAlly. All rights reserved.
// SPDX-License-Identifier: MIT

package repo

import (
	"context"
	"strings"

	"code.gitea.io/gitea/models/db"
	"code.gitea.io/gitea/modules/timeutil"
)

func init() {
	db.RegisterModel(new(WikiIndex))
}

// WikiIndex stores the searchable index for wiki pages
type WikiIndex struct {
	ID          int64              `xorm:"pk autoincr"`
	RepoID      int64              `xorm:"INDEX NOT NULL"`
	PageName    string             `xorm:"VARCHAR(255) NOT NULL"`
	PagePath    string             `xorm:"VARCHAR(512) NOT NULL"` // Git path
	Title       string             `xorm:"VARCHAR(255)"`
	Content     string             `xorm:"LONGTEXT"`    // Full content for search
	ContentHash string             `xorm:"VARCHAR(64)"` // For change detection
	CommitSHA   string             `xorm:"VARCHAR(64)"` // Last indexed commit
	WordCount   int                `xorm:"DEFAULT 0"`
	LinksOut    string             `xorm:"TEXT"` // JSON array of outgoing links
	UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
	CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

// TableName returns the table name for WikiIndex
func (w *WikiIndex) TableName() string {
	return "wiki_index"
}

// GetWikiIndex returns the wiki index for a specific page
func GetWikiIndex(ctx context.Context, repoID int64, pageName string) (*WikiIndex, error) {
	idx := new(WikiIndex)
	has, err := db.GetEngine(ctx).Where("repo_id = ? AND page_name = ?", repoID, pageName).Get(idx)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, nil
	}
	return idx, nil
}

// GetWikiIndexByRepo returns all indexed pages for a repository
func GetWikiIndexByRepo(ctx context.Context, repoID int64) ([]*WikiIndex, error) {
	indexes := make([]*WikiIndex, 0)
	return indexes, db.GetEngine(ctx).Where("repo_id = ?", repoID).OrderBy("page_name ASC").Find(&indexes)
}

// CreateOrUpdateWikiIndex creates or updates a wiki index entry
func CreateOrUpdateWikiIndex(ctx context.Context, idx *WikiIndex) error {
	existing, err := GetWikiIndex(ctx, idx.RepoID, idx.PageName)
	if err != nil {
		return err
	}

	if existing == nil {
		_, err = db.GetEngine(ctx).Insert(idx)
		return err
	}

	idx.ID = existing.ID
	_, err = db.GetEngine(ctx).ID(existing.ID).AllCols().Update(idx)
	return err
}

// DeleteWikiIndex deletes a wiki index entry
func DeleteWikiIndex(ctx context.Context, repoID int64, pageName string) error {
	_, err := db.GetEngine(ctx).Where("repo_id = ? AND page_name = ?", repoID, pageName).Delete(new(WikiIndex))
	return err
}

// DeleteWikiIndexByRepo deletes all wiki indexes for a repository
func DeleteWikiIndexByRepo(ctx context.Context, repoID int64) error {
	_, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Delete(new(WikiIndex))
	return err
}

// SearchWikiOptions contains options for wiki search
type SearchWikiOptions struct {
	RepoID int64
	Query  string
	Limit  int
	Offset int
}

// SearchWikiPages searches wiki pages by content
func SearchWikiPages(ctx context.Context, opts *SearchWikiOptions) ([]*WikiIndex, int64, error) {
	if opts.Limit <= 0 {
		opts.Limit = 20
	}

	sess := db.GetEngine(ctx).Where("repo_id = ?", opts.RepoID)

	// Simple LIKE search - searches title and content
	if opts.Query != "" {
		query := "%" + strings.ToLower(opts.Query) + "%"
		sess = sess.And("(LOWER(title) LIKE ? OR LOWER(content) LIKE ?)", query, query)
	}

	// Get total count
	total, err := sess.Count(new(WikiIndex))
	if err != nil {
		return nil, 0, err
	}

	// Get results
	indexes := make([]*WikiIndex, 0, opts.Limit)
	err = sess.Limit(opts.Limit, opts.Offset).Find(&indexes)
	if err != nil {
		return nil, 0, err
	}

	return indexes, total, nil
}

// GetWikiIndexCount returns the number of indexed pages for a repository
func GetWikiIndexCount(ctx context.Context, repoID int64) (int64, error) {
	return db.GetEngine(ctx).Where("repo_id = ?", repoID).Count(new(WikiIndex))
}

// GetWikiTotalWordCount returns the total word count for a repository's wiki
func GetWikiTotalWordCount(ctx context.Context, repoID int64) (int64, error) {
	total, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).SumInt(new(WikiIndex), "word_count")
	return total, err
}
