use anyhow::{anyhow, ensure, Result};
use bytemuck::{bytes_of, cast_slice, Pod};
use num_complex::Complex;
use runmat_accelerate_api::{GpuTensorHandle, GpuTensorStorage, HostTensorOwned};
use runmat_runtime::builtins::common::shape::normalize_scalar_shape;
use rustfft::FftPlanner;
use std::sync::Arc;

use crate::backend::wgpu::resources::UniformBufferKey;
use crate::backend::wgpu::types::NumericPrecision;

use super::WgpuProvider;

fn fft_trim_trailing_ones(shape: &mut Vec<usize>, minimum_rank: usize) {
    while shape.len() > minimum_rank && shape.last() == Some(&1) {
        shape.pop();
    }
    *shape = normalize_scalar_shape(shape);
}

fn fft_is_power_of_two(len: usize) -> bool {
    len != 0 && (len & (len - 1)) == 0
}

fn fft_log2_pow2(len: usize) -> Option<u32> {
    if !fft_is_power_of_two(len) {
        return None;
    }
    Some(len.trailing_zeros())
}

fn fft_log3_pow3(mut len: usize) -> Option<u32> {
    if len == 0 {
        return None;
    }
    let mut d = 0u32;
    while len > 1 {
        if !len.is_multiple_of(3) {
            return None;
        }
        len /= 3;
        d += 1;
    }
    Some(d)
}

fn fft_log5_pow5(mut len: usize) -> Option<u32> {
    if len == 0 {
        return None;
    }
    let mut d = 0u32;
    while len > 1 {
        if !len.is_multiple_of(5) {
            return None;
        }
        len /= 5;
        d += 1;
    }
    Some(d)
}

fn fft_factor_smooth_235(mut len: usize) -> Option<Vec<u32>> {
    if len <= 1 {
        return None;
    }
    let mut factors = Vec::new();
    while len.is_multiple_of(5) {
        factors.push(5);
        len /= 5;
    }
    while len.is_multiple_of(3) {
        factors.push(3);
        len /= 3;
    }
    while len.is_multiple_of(2) {
        factors.push(2);
        len /= 2;
    }
    if len == 1 && !factors.is_empty() {
        Some(factors)
    } else {
        None
    }
}

mod fallback;
mod helpers;
mod kernels;
