use std::{collections::HashMap, env, fs, path::Path};

use schemars::{JsonSchema, Schema, SchemaGenerator, generate::SchemaSettings};
use services::services::config::{DEFAULT_COMMIT_REMINDER_PROMPT, DEFAULT_PR_DESCRIPTION_PROMPT};
use ts_rs::TS;

fn generate_types_content() -> String {
    // 4. Friendly banner
    const HEADER: &str = "// This file was generated by `crates/core/src/bin/generate_types.rs`.\n
// Do not edit this file manually.\n
// If you are an AI, and you absolutely have to edit this file, please confirm with the user first.";

    let decls: Vec<String> = vec![
        db::models::repo::Repo::decl(),
        db::models::project::Project::decl(),
        db::models::repo::UpdateRepo::decl(),
        db::models::repo::SearchResult::decl(),
        db::models::repo::SearchMatchType::decl(),
        db::models::workspace_repo::WorkspaceRepo::decl(),
        db::models::workspace_repo::CreateWorkspaceRepo::decl(),
        db::models::workspace_repo::RepoWithTargetBranch::decl(),
        db::models::tag::Tag::decl(),
        db::models::tag::CreateTag::decl(),
        db::models::tag::UpdateTag::decl(),
        db::models::scratch::DraftFollowUpData::decl(),
        db::models::scratch::DraftWorkspaceData::decl(),
        db::models::scratch::DraftWorkspaceAttachment::decl(),
        db::models::scratch::DraftWorkspaceLinkedIssue::decl(),
        db::models::scratch::DraftWorkspaceRepo::decl(),
        db::models::scratch::DraftIssueData::decl(),
        db::models::scratch::PreviewSettingsData::decl(),
        db::models::scratch::WorkspaceNotesData::decl(),
        db::models::scratch::WorkspacePanelStateData::decl(),
        db::models::scratch::WorkspacePrFilterData::decl(),
        db::models::scratch::WorkspaceSortByData::decl(),
        db::models::scratch::WorkspaceSortOrderData::decl(),
        db::models::scratch::WorkspaceFilterStateData::decl(),
        db::models::scratch::WorkspaceSortStateData::decl(),
        db::models::scratch::UiPreferencesData::decl(),
        db::models::scratch::ProjectRepoDefaultsData::decl(),
        db::models::scratch::ScratchPayload::decl(),
        db::models::scratch::ScratchType::decl(),
        db::models::scratch::Scratch::decl(),
        db::models::scratch::CreateScratch::decl(),
        db::models::scratch::UpdateScratch::decl(),
        db::models::workspace::Workspace::decl(),
        db::models::workspace::WorkspaceWithStatus::decl(),
        db::models::session::Session::decl(),
        db::models::execution_process::ExecutionProcess::decl(),
        db::models::execution_process::ExecutionProcessStatus::decl(),
        db::models::execution_process::ExecutionProcessRunReason::decl(),
        db::models::execution_process_repo_state::ExecutionProcessRepoState::decl(),
        db::models::merge::Merge::decl(),
        db::models::merge::DirectMerge::decl(),
        db::models::merge::PrMerge::decl(),
        db::models::merge::MergeStatus::decl(),
        db::models::merge::PullRequestInfo::decl(),
        services::services::approvals::ApprovalInfo::decl(),
        utils::approvals::ApprovalStatus::decl(),
        utils::approvals::QuestionAnswer::decl(),
        utils::approvals::QuestionStatus::decl(),
        utils::approvals::ApprovalOutcome::decl(),
        utils::approvals::ApprovalResponse::decl(),
        utils::diff::Diff::decl(),
        utils::diff::DiffChangeKind::decl(),
        utils::response::ApiResponse::<()>::decl(),
        api_types::LoginStatus::decl(),
        api_types::ProfileResponse::decl(),
        api_types::ProviderProfile::decl(),
        api_types::StatusResponse::decl(),
        api_types::MemberRole::decl(),
        api_types::InvitationStatus::decl(),
        api_types::Organization::decl(),
        api_types::OrganizationWithRole::decl(),
        api_types::ListOrganizationsResponse::decl(),
        api_types::GetOrganizationResponse::decl(),
        api_types::CreateOrganizationRequest::decl(),
        api_types::CreateOrganizationResponse::decl(),
        api_types::UpdateOrganizationRequest::decl(),
        api_types::Invitation::decl(),
        api_types::CreateInvitationRequest::decl(),
        api_types::CreateInvitationResponse::decl(),
        api_types::ListInvitationsResponse::decl(),
        api_types::GetInvitationResponse::decl(),
        api_types::AcceptInvitationResponse::decl(),
        api_types::RevokeInvitationRequest::decl(),
        api_types::OrganizationMemberInfo::decl(),
        api_types::OrganizationMemberWithProfile::decl(),
        api_types::ListMembersResponse::decl(),
        api_types::UpdateMemberRoleRequest::decl(),
        api_types::UpdateMemberRoleResponse::decl(),
        server::routes::repo::RegisterRepoRequest::decl(),
        server::routes::repo::InitRepoRequest::decl(),
        server::routes::tags::TagSearchParams::decl(),
        server::routes::oauth::TokenResponse::decl(),
        server::routes::config::UserSystemInfo::decl(),
        server::routes::config::Environment::decl(),
        server::routes::config::McpServerQuery::decl(),
        server::routes::config::UpdateMcpServersBody::decl(),
        server::routes::config::GetMcpServerResponse::decl(),
        server::routes::config::CheckEditorAvailabilityQuery::decl(),
        server::routes::config::CheckEditorAvailabilityResponse::decl(),
        server::routes::config::CheckAgentAvailabilityQuery::decl(),
        server::routes::config::AgentPresetOptionsQuery::decl(),
        server::routes::oauth::CurrentUserResponse::decl(),
        relay_types::StartSpake2EnrollmentRequest::decl(),
        relay_types::FinishSpake2EnrollmentRequest::decl(),
        relay_types::StartSpake2EnrollmentResponse::decl(),
        relay_types::FinishSpake2EnrollmentResponse::decl(),
        relay_types::RelayPairedClient::decl(),
        relay_types::ListRelayPairedClientsResponse::decl(),
        relay_types::RemoveRelayPairedClientResponse::decl(),
        relay_types::RefreshRelaySigningSessionRequest::decl(),
        relay_types::RefreshRelaySigningSessionResponse::decl(),
        server::routes::sessions::CreateFollowUpAttempt::decl(),
        server::routes::sessions::ResetProcessRequest::decl(),
        server::routes::workspaces::git::ChangeTargetBranchRequest::decl(),
        server::routes::workspaces::git::ChangeTargetBranchResponse::decl(),
        server::routes::workspaces::repos::AddWorkspaceRepoRequest::decl(),
        server::routes::workspaces::repos::AddWorkspaceRepoResponse::decl(),
        server::routes::workspaces::git::MergeWorkspaceRequest::decl(),
        server::routes::workspaces::git::PushWorkspaceRequest::decl(),
        server::routes::workspaces::git::RenameBranchRequest::decl(),
        server::routes::workspaces::git::RenameBranchResponse::decl(),
        server::routes::sessions::review::StartReviewRequest::decl(),
        server::routes::sessions::review::ReviewError::decl(),
        server::routes::workspaces::integration::OpenEditorRequest::decl(),
        server::routes::workspaces::integration::OpenEditorResponse::decl(),
        desktop_bridge::service::OpenRemoteEditorResponse::decl(),
        server::routes::host_relay::OpenRemoteWorkspaceInEditorRequest::decl(),
        relay_types::PairRelayHostRequest::decl(),
        relay_types::PairRelayHostResponse::decl(),
        relay_types::RelayPairedHost::decl(),
        relay_types::ListRelayPairedHostsResponse::decl(),
        relay_types::RemoveRelayPairedHostResponse::decl(),
        db::models::requests::CreateWorkspaceApiRequest::decl(),
        db::models::requests::LinkedIssueInfo::decl(),
        server::routes::workspaces::pr::CreatePrApiRequest::decl(),
        server::routes::attachments::AttachmentResponse::decl(),
        server::routes::attachments::AttachmentMetadata::decl(),
        db::models::requests::WorkspaceRepoInput::decl(),
        server::routes::workspaces::integration::RunAgentSetupRequest::decl(),
        server::routes::workspaces::integration::RunAgentSetupResponse::decl(),
        server::routes::workspaces::gh_cli_setup::GhCliSetupError::decl(),
        server::routes::workspaces::git::RebaseWorkspaceRequest::decl(),
        server::routes::workspaces::git::ContinueRebaseRequest::decl(),
        server::routes::workspaces::git::AbortConflictsRequest::decl(),
        server::routes::workspaces::git::GitOperationError::decl(),
        server::routes::workspaces::git::PushError::decl(),
        server::routes::workspaces::pr::PrError::decl(),
        server::routes::workspaces::execution::RunScriptError::decl(),
        server::routes::workspaces::attachments::AssociateWorkspaceAttachmentsRequest::decl(),
        server::routes::workspaces::attachments::ImportIssueAttachmentsRequest::decl(),
        server::routes::workspaces::attachments::ImportIssueAttachmentsResponse::decl(),
        server::routes::workspaces::pr::AttachPrResponse::decl(),
        server::routes::workspaces::pr::AttachExistingPrRequest::decl(),
        server::routes::workspaces::pr::PrCommentsResponse::decl(),
        server::routes::workspaces::pr::GetPrCommentsError::decl(),
        server::routes::workspaces::pr::GetPrCommentsQuery::decl(),
        db::models::requests::CreateAndStartWorkspaceRequest::decl(),
        db::models::requests::CreateAndStartWorkspaceResponse::decl(),
        git_host::UnifiedPrComment::decl(),
        git_host::ProviderKind::decl(),
        git_host::PullRequestDetail::decl(),
        git::GitRemote::decl(),
        server::routes::repo::ListPrsError::decl(),
        server::routes::remote::pull_requests::LinkPrToIssueRequest::decl(),
        server::routes::workspaces::pr::CreateWorkspaceFromPrBody::decl(),
        server::routes::workspaces::pr::CreateWorkspaceFromPrResponse::decl(),
        server::routes::workspaces::pr::CreateFromPrError::decl(),
        server::routes::workspaces::git::RepoBranchStatus::decl(),
        db::models::requests::UpdateWorkspace::decl(),
        db::models::requests::UpdateSession::decl(),
        server::routes::workspaces::workspace_summary::WorkspaceSummaryRequest::decl(),
        server::routes::workspaces::workspace_summary::WorkspaceSummary::decl(),
        server::routes::workspaces::workspace_summary::WorkspaceSummaryResponse::decl(),
        server::routes::workspaces::workspace_summary::DiffStats::decl(),
        services::services::filesystem::DirectoryEntry::decl(),
        services::services::filesystem::DirectoryListResponse::decl(),
        services::services::file_search::SearchMode::decl(),
        services::services::config::Config::decl(),
        services::services::config::NotificationConfig::decl(),
        services::services::config::ThemeMode::decl(),
        services::services::config::EditorConfig::decl(),
        services::services::config::EditorType::decl(),
        services::services::config::EditorOpenError::decl(),
        services::services::config::GitHubConfig::decl(),
        services::services::config::SoundFile::decl(),
        services::services::config::UiLanguage::decl(),
        services::services::config::ShowcaseState::decl(),
        services::services::config::SendMessageShortcut::decl(),
        git::GitBranch::decl(),
        services::services::queued_message::QueuedMessage::decl(),
        services::services::queued_message::QueueStatus::decl(),
        git::ConflictOp::decl(),
        executors::actions::ExecutorAction::decl(),
        executors::mcp_config::McpConfig::decl(),
        executors::actions::ExecutorActionType::decl(),
        executors::profile::ExecutorConfig::decl(),
        executors::actions::script::ScriptContext::decl(),
        executors::actions::script::ScriptRequest::decl(),
        executors::actions::script::ScriptRequestLanguage::decl(),
        executors::executors::BaseCodingAgent::decl(),
        executors::executors::CodingAgent::decl(),
        executors::executors::SlashCommandDescription::decl(),
        executors::executors::AvailabilityInfo::decl(),
        executors::command::CommandBuilder::decl(),
        executors::profile::ExecutorProfileId::decl(),
        executors::profile::ExecutorRecentModels::decl(),
        executors::profile::ExecutorProfile::decl(),
        executors::profile::ExecutorConfigs::decl(),
        executors::executors::BaseAgentCapability::decl(),
        executors::executors::claude::ClaudeEffort::decl(),
        executors::executors::claude::ClaudeCode::decl(),
        executors::executors::gemini::Gemini::decl(),
        executors::executors::amp::Amp::decl(),
        executors::executors::codex::Codex::decl(),
        executors::executors::codex::SandboxMode::decl(),
        executors::executors::codex::AskForApproval::decl(),
        executors::executors::codex::ReasoningEffort::decl(),
        executors::executors::codex::ReasoningSummary::decl(),
        executors::executors::codex::ReasoningSummaryFormat::decl(),
        executors::executors::cursor::CursorAgent::decl(),
        executors::executors::copilot::Copilot::decl(),
        executors::executors::opencode::Opencode::decl(),
        executors::executors::qwen::QwenCode::decl(),
        executors::executors::droid::Droid::decl(),
        executors::executors::droid::Autonomy::decl(),
        executors::executors::droid::ReasoningEffortLevel::decl(),
        executors::executors::AppendPrompt::decl(),
        executors::actions::coding_agent_initial::CodingAgentInitialRequest::decl(),
        executors::actions::coding_agent_follow_up::CodingAgentFollowUpRequest::decl(),
        executors::actions::review::ReviewRequest::decl(),
        executors::actions::review::RepoReviewContext::decl(),
        executors::logs::CommandExitStatus::decl(),
        executors::logs::CommandRunResult::decl(),
        executors::logs::utils::shell_command_parsing::CommandCategory::decl(),
        executors::logs::NormalizedEntry::decl(),
        executors::logs::NormalizedEntryType::decl(),
        executors::logs::TokenUsageInfo::decl(),
        executors::logs::FileChange::decl(),
        executors::logs::ActionType::decl(),
        executors::logs::AnsweredQuestion::decl(),
        executors::logs::AskUserQuestionItem::decl(),
        executors::logs::AskUserQuestionOption::decl(),
        executors::logs::TodoItem::decl(),
        executors::logs::NormalizedEntryError::decl(),
        executors::logs::ToolResult::decl(),
        executors::logs::ToolResultValueType::decl(),
        executors::logs::ToolStatus::decl(),
        executors::logs::utils::patch::PatchType::decl(),
        executors::model_selector::ModelInfo::decl(),
        executors::model_selector::ReasoningOption::decl(),
        executors::model_selector::ModelProvider::decl(),
        executors::model_selector::AgentInfo::decl(),
        executors::model_selector::PermissionPolicy::decl(),
        executors::model_selector::ModelSelectorConfig::decl(),
        executors::executor_discovery::ExecutorDiscoveredOptions::decl(),
        serde_json::Value::decl(),
        relay_protocol::RelayWsMessageType::decl(),
        relay_webrtc::DataChannelMessage::decl(),
        relay_webrtc::DataChannelRequest::decl(),
        relay_webrtc::DataChannelResponse::decl(),
        relay_webrtc::WsOpen::decl(),
        relay_webrtc::WsOpened::decl(),
        relay_webrtc::WsFrame::decl(),
        relay_webrtc::WsClose::decl(),
        relay_webrtc::WsError::decl(),
        relay_webrtc::SdpOffer::decl(),
        relay_webrtc::SdpAnswer::decl(),
    ];

    let body = decls
        .into_iter()
        .map(|d| {
            let trimmed = d.trim_start();
            if trimmed.starts_with("export") {
                d
            } else {
                format!("export {trimmed}")
            }
        })
        .collect::<Vec<_>>()
        .join("\n\n");

    // Append exported constants
    let constants = format!(
        "export const DEFAULT_PR_DESCRIPTION_PROMPT = {};\n\nexport const DEFAULT_COMMIT_REMINDER_PROMPT = {};",
        serde_json::to_string(DEFAULT_PR_DESCRIPTION_PROMPT).unwrap(),
        serde_json::to_string(DEFAULT_COMMIT_REMINDER_PROMPT).unwrap()
    );

    format!("{HEADER}\n\n{body}\n\n{constants}")
}

fn generate_json_schema<T: JsonSchema>() -> Result<String, serde_json::Error> {
    // Draft-07, inline everything (no $defs)
    let mut settings = SchemaSettings::draft07();
    settings.inline_subschemas = true;

    let generator: SchemaGenerator = settings.into_generator();
    let schema: Schema = generator.into_root_schema_for::<T>();

    // Convert to JSON value to manipulate it
    let mut schema_value: serde_json::Value = serde_json::to_value(&schema)?;
    // Remove the title from root schema to prevent RJSF from creating an outer field container
    if let Some(obj) = schema_value.as_object_mut() {
        obj.remove("title");
    }
    let formatted = serde_json::to_string_pretty(&schema_value)?;
    Ok(formatted)
}

fn generate_schemas() -> Result<HashMap<&'static str, String>, serde_json::Error> {
    // // Generate schemas for all executor types
    println!("Generating JSON schemas…");
    let schemas: HashMap<&str, String> = HashMap::from([
        (
            "amp",
            generate_json_schema::<executors::executors::amp::Amp>()?,
        ),
        (
            "claude_code",
            generate_json_schema::<executors::executors::claude::ClaudeCode>()?,
        ),
        (
            "gemini",
            generate_json_schema::<executors::executors::gemini::Gemini>()?,
        ),
        (
            "codex",
            generate_json_schema::<executors::executors::codex::Codex>()?,
        ),
        (
            "cursor_agent",
            generate_json_schema::<executors::executors::cursor::CursorAgent>()?,
        ),
        (
            "opencode",
            generate_json_schema::<executors::executors::opencode::Opencode>()?,
        ),
        (
            "qwen_code",
            generate_json_schema::<executors::executors::qwen::QwenCode>()?,
        ),
        (
            "copilot",
            generate_json_schema::<executors::executors::copilot::Copilot>()?,
        ),
        (
            "droid",
            generate_json_schema::<executors::executors::droid::Droid>()?,
        ),
    ]);
    println!(
        "✅ JSON schemas generated. {} schemas created.",
        schemas.len()
    );
    Ok(schemas)
}

fn write_schemas(
    schemas_path: &Path,
    schemas: HashMap<&str, String>,
) -> Result<(), Box<dyn std::error::Error>> {
    fs::create_dir_all(schemas_path)?;

    for (name, content) in schemas {
        let schema_file = schemas_path.join(format!("{}.json", name));
        fs::write(&schema_file, content)?;
        println!("✅ Generated schema: {}", schema_file.display());
    }

    Ok(())
}

fn schemas_up_to_date(schemas_path: &Path, schemas: &HashMap<&str, String>) -> bool {
    for (name, expected_content) in schemas {
        let schema_file = schemas_path.join(format!("{}.json", name));
        let current_content = fs::read_to_string(&schema_file).unwrap_or_default();
        if &current_content != expected_content {
            eprintln!("❌ Schema shared/schemas/{}.json is not up to date.", name);
            return false;
        }
    }
    true
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let check_mode = args.iter().any(|arg| arg == "--check");

    let shared_path = Path::new("shared");

    println!("Generating TypeScript types…");

    let generated_types = generate_types_content();
    let schema_content = match generate_schemas() {
        Ok(s) => s,
        Err(e) => {
            eprintln!("❌ Failed to generate JSON schemas: {}", e);
            std::process::exit(1);
        }
    };

    let types_path = shared_path.join("types.ts");
    let schemas_path = shared_path.join("schemas");

    if check_mode {
        // Check TypeScript types
        let current = fs::read_to_string(&types_path).unwrap_or_default();
        let types_up_to_date = if current == generated_types {
            println!("✅ shared/types.ts is up to date.");
            true
        } else {
            eprintln!("❌ shared/types.ts is not up to date.");
            false
        };

        // Check JSON schemas
        let schemas_up_to_date = schemas_up_to_date(&schemas_path, &schema_content);

        // Exit with appropriate code
        if types_up_to_date && schemas_up_to_date {
            std::process::exit(0);
        } else {
            eprintln!("Please run 'npm run generate-types' and commit the changes.");
            std::process::exit(1);
        }
    } else {
        fs::create_dir_all(shared_path).expect("cannot create shared");

        fs::remove_file(&types_path).ok();
        fs::remove_dir_all(&schemas_path).ok();

        fs::write(&types_path, generated_types).expect("unable to write types.ts");
        println!("✅ TypeScript types generated in shared/types.ts");

        write_schemas(&schemas_path, schema_content).expect("unable to write schemas");

        println!("✅ JSON schemas generated in shared/schemas/");
    }
}
