// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers
// SPDX-License-Identifier: Apache-2.0

package codescan

import (
	"fmt"
	"go/token"

	"github.com/go-openapi/codescan/internal/builders/spec"
	"github.com/go-openapi/codescan/internal/parsers/grammar"
	"github.com/go-openapi/codescan/internal/scanner"
	oaispec "github.com/go-openapi/spec"
)

// Options for the scanner.
type Options = scanner.Options

// Run the scanner to produce a swagger spec with the options provided.
func Run(opts *Options) (_ *oaispec.Swagger, err error) {
	// proposal for improvement (maintainers): use option functors pattern
	//
	// Last-resort backstop: the spec builder wraps each per-declaration step under a located panic
	// guard (scan.internal-panic), but a panic outside any decl loop (e.g. name reduction, the package
	// walk) would still escape as a raw stack trace.
	// Convert it into a clean error and a diagnostic instead.
	defer func() {
		if r := recover(); r != nil {
			if opts != nil && opts.OnDiagnostic != nil {
				opts.OnDiagnostic(grammar.Errorf(token.Position{}, grammar.CodeInternalPanic,
					"unrecovered panic during scan: %v", r))
			}
			err = fmt.Errorf("unrecovered panic during scan: %v: %w", r, ErrCodeScan)
		}
	}()

	ctx, err := scanner.NewScanCtx(opts)
	if err != nil {
		return nil, fmt.Errorf("could not scan source: %w: %w", err, ErrCodeScan)
	}

	builder := spec.NewBuilder(opts.InputSpec, ctx, opts.ScanModels)
	sp, err := builder.Build()
	if err != nil {
		return nil, fmt.Errorf("could not build spec: %w: %w", err, ErrCodeScan)
	}

	return sp, nil
}
