package main

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/aweris/gale/common/model"
)

// FIXME: Report structs are modified copies of the structs in ghx/context/reports.go to workaround the dagger's
//  not supporting map types. Once dagger supports map types, these structs should be moved to common package and
//  used in both ghx and gale.

// TODO: Removed 'Matrix     model.MatrixCombination` from report since it's not possible use map and don't know
//  how to handle interface{} in dagger. Find way to handle this.

type WorkflowRunReport struct {
	Ran        bool             // Ran indicates if the execution ran
	Duration   string           // Duration of the execution
	Name       string           // Name is the name of the workflow
	Conclusion model.Conclusion // Conclusion is the result of a completed workflow run after continue-on-error is applied
	File       *File            // File is the report file contains  json report of the workflow
}

// NewWorkflowRunReport creates a new WorkflowRunReport from the given parameters.
func NewWorkflowRunReport(
	ran bool,
	runID string,
	workflow *Workflow,
	conclusion model.Conclusion,
	duration time.Duration,
	jrs []*JobRun,
) (*WorkflowRunReport, error) {
	var jobs = make(map[string]model.Conclusion)

	for _, jr := range jrs {
		jobs[jr.Job.JobID] = jr.Report.Conclusion
	}

	wm := &model.WorkflowRunReport{
		Ran:        ran,
		Duration:   duration.String(),
		Name:       workflow.Name,
		Path:       workflow.Path,
		RunID:      runID,
		Conclusion: conclusion,
		Jobs:       jobs,
	}

	data, err := json.Marshal(wm)
	if err != nil {
		return nil, err
	}

	file := dag.Directory().WithNewFile("workflow_run.json", string(data)).File("workflow_run.json")

	return &WorkflowRunReport{
		Ran:        ran,
		Duration:   duration.String(),
		Name:       workflow.Name,
		Conclusion: conclusion,
		File:       file,
	}, nil
}

type JobRunReport struct {
	Ran        bool             // Ran indicates if the execution ran
	Duration   string           // Duration of the execution
	Name       string           // Name is the name of the job
	RunID      string           // RunID is the ID of the run
	Conclusion model.Conclusion // Conclusion is the result of a completed job after continue-on-error is applied
	Outcome    model.Conclusion // Outcome is  the result of a completed job before continue-on-error is applied
	Outputs    []KV             // Outputs is the outputs generated by the job
	Steps      []StepRunSummary // Steps is the list of steps in the job
	File       *File            // File is the report file contains original json report of the job
}

type StepRunSummary struct {
	StepID     string           // ID is the unique identifier of the step.
	Name       string           // Name is the name of the step
	Stage      model.StepStage  // Stage is the stage of the step during the execution of the job. Possible values are: setup, pre, main, post, complete.
	Conclusion model.Conclusion // Conclusion is the result of a completed job after continue-on-error is applied
}

// parseJobRunReport converts the report file to JobRunReport struct
func parseJobRunReport(ctx context.Context, file *File) (*JobRunReport, error) {
	contents, err := file.Contents(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to read report file: %w", err)
	}

	// original report model to unmarshal the report file
	var rm model.JobRunReport

	if err := json.Unmarshal([]byte(contents), &rm); err != nil {
		return nil, fmt.Errorf("failed to unmarshal report file: %w", err)
	}

	// convert model types to module types since dagger doesn't support map types and native reporting types
	// slightly different from the model types
	steps := make([]StepRunSummary, 0, len(rm.Steps))

	for _, step := range rm.Steps {
		steps = append(steps, convertStepRunSummary(step))
	}

	report := &JobRunReport{
		Ran:        rm.Ran,
		Duration:   rm.Duration,
		Name:       rm.Name,
		RunID:      rm.RunID,
		Conclusion: rm.Conclusion,
		Outcome:    rm.Outcome,
		Outputs:    ConvertMapToKVSlice(rm.Outputs),
		Steps:      steps,
		File:       file,
	}

	return report, nil
}

// convertStepRunSummary converts model.StepRunSummary to StepRunSummary
func convertStepRunSummary(srs model.StepRunSummary) StepRunSummary {
	return StepRunSummary{
		StepID:     srs.ID,
		Name:       srs.Name,
		Stage:      srs.Stage,
		Conclusion: srs.Conclusion,
	}
}
