package provider

import (
	"context"
	"fmt"

	"terraform-provider-microsoftfabric/internal/apiclient"

	"github.com/hashicorp/terraform-plugin-framework/resource"
	"github.com/hashicorp/terraform-plugin-framework/resource/schema"
	"github.com/hashicorp/terraform-plugin-framework/types"
)

// kqlDatabaseResource defines the resource structure for managing Kql Databases.
type kqlDatabaseResource struct {
	client *apiclient.APIClient // API client for making requests.
}

// Schema defines the schema for the Kql Database resource.
func (r *kqlDatabaseResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			"id": schema.StringAttribute{
				Computed: true, // ID is generated by the API upon creation.
			},
			"workspace_id": schema.StringAttribute{
				Required: true, // The unique identifier for the workspace.
			},
			"display_name": schema.StringAttribute{
				Required: true, // The name of the Kql Database.
			},
			"description": schema.StringAttribute{
				Optional: true, // Optional description for the Kql Database.
			},
			"creation_payload": schema.SingleNestedAttribute{
				Required: true,
				Attributes: map[string]schema.Attribute{
					"database_type": schema.StringAttribute{
						Required:    true,
						Description: "Database type (e.g. ReadWrite or Shortcut).",
					},
					"parent_eventhouse_items_id": schema.StringAttribute{
						Required:    true,
						Description: "The ID of the Eventhouse where the KQL is in.",
					},
					"invitation_token": schema.StringAttribute{
						Optional:    true,
						Description: "Invitation token to follow the source database.",
					},
					"source_cluster_uri": schema.StringAttribute{
						Optional:    true,
						Description: "The URI of the source Eventhouse or Azure Data Explorer cluster.",
					},
					"source_database_name": schema.StringAttribute{
						Optional:    true,
						Description: "The name of the database to follow in the source Eventhouse or Azure Data Explorer cluster.",
					},
				},
			},
		},
	}
}

// kqlDatabaseResourceModel defines the model for managing the Kql Database's state.
type kqlDatabaseResourceModel struct {
	ID              types.String         `tfsdk:"id"`               // Unique identifier for the Kql Database
	WorkspaceID     types.String         `tfsdk:"workspace_id"`     // ID of the workspace the Kql Database belongs to (same as the parent Eventhouse)
	DisplayName     types.String         `tfsdk:"display_name"`     // Display name of the Kql Database
	Description     types.String         `tfsdk:"description"`      // Description of the Kql Database
	CreationPayload creationPayloadModel `tfsdk:"creation_payload"` // Payload for creation
}

type creationPayloadModel struct {
	DatabaseType           types.String `tfsdk:"database_type"`
	ParentEventhouseItemId types.String `tfsdk:"parent_eventhouse_items_id"`
	InvitationToken        types.String `tfsdk:"invitation_token"`
	SourceClusterUri       types.String `tfsdk:"source_cluster_uri"`
	SourceDatabaseName     types.String `tfsdk:"source_database_name"`
}

// Metadata returns metadata about the Eventhouse resource.
func (r *kqlDatabaseResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
	resp.TypeName = "microsoftfabric_kqldatabase" // Name used in Terraform configuration
}

// NewKqlDatabaseResource creates a new Eventhouse resource.
func NewKqlDatabaseResource(client *apiclient.APIClient) resource.Resource {
	return &kqlDatabaseResource{client: client} // Initialize with API client
}

// Create implements the creation of an Eventhouse resource.
func (r *kqlDatabaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
	// Retrieve the planned state of the resource
	var plan kqlDatabaseResourceModel
	diags := req.Plan.Get(ctx, &plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	// Create the  Kql Database via the API client
	kqlDatabaseID, err := r.createKqlDatabase(plan.WorkspaceID.ValueString(), plan.DisplayName.ValueString(), plan.Description.ValueString(), plan.CreationPayload.DatabaseType.ValueString(), plan.CreationPayload.ParentEventhouseItemId.ValueString(), plan.CreationPayload.InvitationToken.ValueString(), plan.CreationPayload.SourceClusterUri.ValueString(), plan.CreationPayload.SourceDatabaseName.ValueString())
	if err != nil {
		// Add error diagnostics if creation fails
		resp.Diagnostics.AddError(
			"Error creating Kql Database",
			"Could not create  Kql Database: "+err.Error(),
		)
		return
	}

	// Set the ID and LastUpdated fields
	plan.ID = types.StringValue(kqlDatabaseID) // Set the ID returned from the API
	//plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850)) // Current timestamp

	// Set the state of the resource in Terraform
	diags = resp.State.Set(ctx, plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
}

// Read retrieves the current state of an Eventhouse resource.
func (r *kqlDatabaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
	// Retrieve the current state from the resource
	var state kqlDatabaseResourceModel
	diags := req.State.Get(ctx, &state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	// Read the Eventhouse details from the API
	eventhouse, err := r.readKqlDatabase(state.WorkspaceID.ValueString(), state.ID.ValueString())
	if err != nil {
		// Add error diagnostics if reading fails
		resp.Diagnostics.AddError(
			"Error reading Eventhouse",
			"Could not read Eventhouse: "+err.Error(),
		)
		return
	}

	// Update state with the values retrieved from the API
	state.DisplayName = types.StringValue(eventhouse.DisplayName.ValueString())
	state.Description = types.StringValue(eventhouse.Description.ValueString())
	//state.LastUpdated = types.StringValue(time.Now().Format(time.RFC850)) // Update last modified timestamp

	diags = resp.State.Set(ctx, state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
}

// Update modifies an existing Eventhouse resource.
func (r *kqlDatabaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
	var plan kqlDatabaseResourceModel
	diags := req.Plan.Get(ctx, &plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	var state kqlDatabaseResourceModel
	diags = req.State.Get(ctx, &state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	// Update the Eventhouse with new details
	err := r.updateKqlDatabase(plan.WorkspaceID.ValueString(), state.ID.ValueString(), plan.DisplayName.ValueString(), plan.Description.ValueString())
	if err != nil {
		// Add error diagnostics if updating fails
		resp.Diagnostics.AddError(
			"Error updating Eventhouse",
			"Could not update Eventhouse: "+err.Error(),
		)
		return
	}

	// Set LastUpdated field to current timestamp
	plan.ID = state.ID // Ensure the ID remains unchanged
	//plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850))

	// Set the updated state
	diags = resp.State.Set(ctx, plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
}

// Delete removes an Eventhouse resource.
func (r *kqlDatabaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
	var state kqlDatabaseResourceModel
	diags := req.State.Get(ctx, &state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	// Remove the Kql Database via the API
	err := r.deleteKqlDatabase(state.WorkspaceID.ValueString(), state.ID.ValueString())
	if err != nil {
		// Add error diagnostics if deletion fails
		resp.Diagnostics.AddError(
			"Error deleting Eventhouse",
			"Could not delete Eventhouse: "+err.Error(),
		)
		return
	}

	// Remove resource from state
	resp.State.RemoveResource(ctx)
}

// createKqlDatabase sends a request to create a new  Kql Database in the specified workspace.
func (r *kqlDatabaseResource) createKqlDatabase(workspaceID, displayName, description, databaseType, parentEventhouseItemId, invitationToken, sourceClusterUri, sourceDatabaseName string) (string, error) {
	// URL for the API endpoint to create an  Kql Database
	url := fmt.Sprintf("https://api.fabric.microsoft.com/v1/workspaces/%s/kqlDatabases", workspaceID)
	body := map[string]interface{}{
		"displayName": displayName, // Set the display name
		"description": description, // Set the description
		"creationPayload": map[string]string{
			"databaseType":           databaseType,
			"parentEventhouseItemId": parentEventhouseItemId,
			"invitationToken":        invitationToken,
			"sourceClusterUri":       sourceClusterUri,
			"sourceDatabaseName":     sourceDatabaseName,
		},
	}

	// Make a POST request to create the  Kql Database
	responseBody, err := r.client.PostWithOperationCheck(url, body)
	if err != nil {
		return "", fmt.Errorf("failed to make POST request: %w", err) // Return the error on failure
	}

	// Extract the  Kql Database ID from the response
	kqlDatabaseID, ok := responseBody["id"].(string) // Modify based on the actual response structure
	if !ok {
		return "", fmt.Errorf("unexpected response format: 'id' key not found") // Handle unexpected response
	}

	return kqlDatabaseID, nil // Return the newly created Kql Database ID
}

// readKqlDatabase retrieves the details of an existing  Kql Database.
func (r *kqlDatabaseResource) readKqlDatabase(workspaceID, kqlDatabaseID string) (kqlDatabaseResourceModel, error) {
	// URL for the API endpoint to read an  Kql Database
	url := fmt.Sprintf("https://api.fabric.microsoft.com/v1/workspaces/%s/kqlDatabases/%s", workspaceID, kqlDatabaseID)

	responseBody, err := r.client.Get(url) // Fetch Kql Database details from API
	if err != nil {
		return kqlDatabaseResourceModel{}, err // Return error if fetching fails
	}

	// Map the response data to the Kql Database model
	kqlDatabase := kqlDatabaseResourceModel{
		ID:          types.StringValue(responseBody["id"].(string)),          // Kql Database ID
		DisplayName: types.StringValue(responseBody["displayName"].(string)), // Kql Database display name
		Description: types.StringValue(responseBody["description"].(string)), // Kql Database description
	}

	return kqlDatabase, nil // Return the populated Kql Database model
}

// updateKqlDatabase sends a request to update an existing Kql Database.
func (r *kqlDatabaseResource) updateKqlDatabase(workspaceID, kqlDatabaseID, displayName, description string) error {
	// URL for the API endpoint to update an Kql Database
	url := fmt.Sprintf("https://api.fabric.microsoft.com/v1/workspaces/%s/kqlDatabases/%s", workspaceID, kqlDatabaseID)
	body := map[string]interface{}{
		"displayName": displayName, // New display name for the Kql Database
		"description": description, // New description for the Kql Database
	}

	_, err := r.client.Patch(url, body) // Make PATCH request to update
	return err                          // Return any error encountered
}

// deleteKqlDatabase sends a request to delete an existing Kql Database.
func (r *kqlDatabaseResource) deleteKqlDatabase(workspaceID, kqlDatabaseID string) error {
	// URL for the API endpoint to delete an Kql Database
	url := fmt.Sprintf("https://api.fabric.microsoft.com/v1/workspaces/%s/kqlDatabases/%s", workspaceID, kqlDatabaseID)
	return r.client.Delete(url) // Make DELETE request and return any error
}
