#!/usr/bin/env perl
#
# Test XS::JIT::Header write_module and write_c_file functionality
#

use strict;
use warnings;
use Test::More;
use File::Temp qw(tempdir);
use File::Spec;

use_ok('XS::JIT::Header');

my $temp_dir = tempdir(CLEANUP => 1);

# Create a test header file
my $header_file = File::Spec->catfile($temp_dir, 'mylib.h');
open my $fh, '>', $header_file or die "Cannot create $header_file: $!";
print $fh <<'HEADER';
/* Test library header */

#define MYLIB_VERSION 1
#define MYLIB_PI 3.14159

/* Simple functions */
int mylib_add(int a, int b);
double mylib_multiply(double x, double y);
const char* mylib_version(void);
void mylib_init(void);

HEADER
close $fh;

subtest 'write_c_file' => sub {
    my $h = XS::JIT::Header->new(
        header    => $header_file,
        package   => 'My::Lib',
        cache_dir => $temp_dir,
    );

    # Attach some functions
    $h->attach('mylib_add');
    $h->attach('mylib_multiply');

    # Write C file
    my $c_file = File::Spec->catfile($temp_dir, 'mylib_wrappers.c');
    $h->write_c_file($c_file);

    ok(-f $c_file, 'C file was created');

    # Check content
    open my $fh, '<', $c_file or die "Cannot read $c_file: $!";
    my $content = do { local $/; <$fh> };
    close $fh;

    like($content, qr/Generated by XS::JIT::Header/, 'has generator comment');
    like($content, qr/#include.*mylib\.h/, 'includes the header');
    like($content, qr/XS_EUPXS\(xs_mylib_add\)/, 'has wrapper for mylib_add');
    like($content, qr/XS_EUPXS\(xs_mylib_multiply\)/, 'has wrapper for mylib_multiply');
    like($content, qr/SvIV/, 'uses SvIV for int conversion');
    like($content, qr/SvNV/, 'uses SvNV for double conversion');
};

subtest 'write_module creates directory structure' => sub {
    my $h = XS::JIT::Header->new(
        header    => $header_file,
        package   => 'My::Lib',
        cache_dir => $temp_dir,
    );

    $h->attach('mylib_add');
    $h->attach('mylib_multiply');
    $h->attach('mylib_version');

    my $output_dir = File::Spec->catdir($temp_dir, 'My-Lib-Dist');
    my $result = $h->write_module(dir => $output_dir);

    ok($result, 'write_module returns result');
    is(ref $result, 'HASH', 'result is a hashref');

    # Check returned paths
    ok(exists $result->{pm_file}, 'result has pm_file');
    ok(exists $result->{xs_file}, 'result has xs_file');
    ok(exists $result->{c_file}, 'result has c_file');
    ok(exists $result->{package}, 'result has package');
    is($result->{package}, 'My::Lib', 'package is correct');

    # Check files exist
    ok(-f $result->{pm_file}, 'PM file exists');
    ok(-f $result->{xs_file}, 'XS file exists');
    ok(-f $result->{c_file}, 'C file exists');

    # Check directory structure
    my $lib_dir = File::Spec->catdir($output_dir, 'lib', 'My');
    ok(-d $lib_dir, 'lib/My directory was created');
};

subtest 'write_module PM file content' => sub {
    my $h = XS::JIT::Header->new(
        header    => $header_file,
        package   => 'Test::MyLib',
        cache_dir => $temp_dir,
    );

    $h->attach('mylib_add');
    $h->attach('mylib_init');

    my $output_dir = File::Spec->catdir($temp_dir, 'Test-MyLib');
    my $result = $h->write_module(dir => $output_dir);

    open my $fh, '<', $result->{pm_file} or die "Cannot read PM: $!";
    my $pm = do { local $/; <$fh> };
    close $fh;

    like($pm, qr/package Test::MyLib;/, 'has correct package declaration');
    like($pm, qr/use strict;/, 'has use strict');
    like($pm, qr/use warnings;/, 'has use warnings');
    like($pm, qr/our \$VERSION/, 'has VERSION');
    like($pm, qr/XSLoader::load/, 'uses XSLoader');
    like($pm, qr/=head1 NAME/, 'has POD NAME section');
    like($pm, qr/=head1 SYNOPSIS/, 'has POD SYNOPSIS section');
    like($pm, qr/=head1 FUNCTIONS/, 'has POD FUNCTIONS section');
    like($pm, qr/mylib_add/, 'documents mylib_add');
};

subtest 'write_module XS file content' => sub {
    my $h = XS::JIT::Header->new(
        header    => $header_file,
        package   => 'Another::Lib',
        cache_dir => $temp_dir,
    );

    $h->attach('mylib_add');
    $h->attach('mylib_multiply');

    my $output_dir = File::Spec->catdir($temp_dir, 'Another-Lib');
    my $result = $h->write_module(dir => $output_dir);

    open my $fh, '<', $result->{xs_file} or die "Cannot read XS: $!";
    my $xs = do { local $/; <$fh> };
    close $fh;

    like($xs, qr/#define PERL_NO_GET_CONTEXT/, 'has PERL_NO_GET_CONTEXT');
    like($xs, qr/#include "EXTERN\.h"/, 'includes EXTERN.h');
    like($xs, qr/#include "perl\.h"/, 'includes perl.h');
    like($xs, qr/#include "XSUB\.h"/, 'includes XSUB.h');
    like($xs, qr/#include "Lib_funcs\.c"/, 'includes the C wrappers file');
    like($xs, qr/MODULE = Another::Lib/, 'has correct MODULE');
    like($xs, qr/PACKAGE = Another::Lib/, 'has correct PACKAGE');
    like($xs, qr/BOOT:/, 'has BOOT section');
    like($xs, qr/newXS\("Another::Lib::mylib_add"/, 'registers mylib_add');
    like($xs, qr/newXS\("Another::Lib::mylib_multiply"/, 'registers mylib_multiply');
};

subtest 'write_module with attach_all' => sub {
    my $h = XS::JIT::Header->new(
        header    => $header_file,
        package   => 'Full::Lib',
        cache_dir => $temp_dir,
    );

    # Attach all functions from header
    $h->attach_all();

    my $output_dir = File::Spec->catdir($temp_dir, 'Full-Lib');
    my $result = $h->write_module(dir => $output_dir);

    ok(-f $result->{c_file}, 'C file created');

    open my $fh, '<', $result->{c_file} or die "Cannot read C: $!";
    my $c = do { local $/; <$fh> };
    close $fh;

    # Should have all 4 functions
    like($c, qr/xs_mylib_add/, 'has mylib_add wrapper');
    like($c, qr/xs_mylib_multiply/, 'has mylib_multiply wrapper');
    like($c, qr/xs_mylib_version/, 'has mylib_version wrapper');
    like($c, qr/xs_mylib_init/, 'has mylib_init wrapper');
};

subtest 'write_module with custom package' => sub {
    my $h = XS::JIT::Header->new(
        header    => $header_file,
        package   => 'Original::Package',
        cache_dir => $temp_dir,
    );

    $h->attach('mylib_add');

    my $output_dir = File::Spec->catdir($temp_dir, 'Custom-Pkg');
    my $result = $h->write_module(
        dir     => $output_dir,
        package => 'Custom::Package',
    );

    is($result->{package}, 'Custom::Package', 'uses custom package');

    open my $fh, '<', $result->{pm_file} or die "Cannot read PM: $!";
    my $pm = do { local $/; <$fh> };
    close $fh;

    like($pm, qr/package Custom::Package;/, 'PM has custom package');

    # Check directory structure matches custom package
    my $expected_dir = File::Spec->catdir($output_dir, 'lib', 'Custom');
    ok(-d $expected_dir, 'directory matches custom package');
};

subtest 'error handling' => sub {
    my $h = XS::JIT::Header->new(
        header    => $header_file,
        package   => 'Error::Test',
        cache_dir => $temp_dir,
    );

    # No functions attached
    eval { $h->write_module(dir => $temp_dir) };
    like($@, qr/No functions attached/, 'dies with no functions attached');

    eval { $h->write_c_file(File::Spec->catfile($temp_dir, 'test.c')) };
    like($@, qr/No functions attached/, 'write_c_file dies with no functions');

    $h->attach('mylib_add');

    eval { $h->write_c_file() };
    like($@, qr/requires a filename/, 'write_c_file requires filename');

    # Prevent auto-compile in DESTROY (no real library to link against)
    $h->{_compiled} = 1;
};

done_testing();
