// pms.c
// Product Management System (simple menu-driven CLI)
// Compile: gcc -std=c99 -Wall -Wextra -o pms pms.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DB_FILENAME "products.dat"
#define MAX_NAME 50
#define MAX_CAT 30
#define MAX_DESC 128

typedef struct {
    int id;
    char name[MAX_NAME];
    char category[MAX_CAT];
    int quantity;
    float price;
    char description[MAX_DESC];
} Product;

/* Utility prototypes */
void clear_input_buffer(void);
int get_next_id(FILE *fp);
void press_enter_to_continue(void);

/* Core operations */
void add_product(void);
void list_products(void);
void search_product(void);
void update_product(void);
void delete_product(void);
void export_text_report(void);

/* File helpers */
int load_all_products(Product **arr, size_t *count);
int save_all_products(Product *arr, size_t count);

int main(void) {
    int choice;
    while (1) {
        printf("\n====== Product Management System ======\n");
        printf("1. Add Product\n");
        printf("2. List All Products\n");
        printf("3. Search Product by ID\n");
        printf("4. Update Product by ID\n");
        printf("5. Delete Product by ID\n");
        printf("6. Export Report (text)\n");
        printf("0. Exit\n");
        printf("Select option: ");
        if (scanf("%d", &choice) != 1) {
            clear_input_buffer();
            printf("Invalid input. Try again.\n");
            continue;
        }
        clear_input_buffer();

        switch (choice) {
            case 1: add_product(); break;
            case 2: list_products(); break;
            case 3: search_product(); break;
            case 4: update_product(); break;
            case 5: delete_product(); break;
            case 6: export_text_report(); break;
            case 0:
                printf("Exiting. Goodbye!\n");
                return 0;
            default:
                printf("Invalid option. Try again.\n");
        }
    }
    return 0;
}

/* ========== Utilities ========== */
void clear_input_buffer(void) {
    int c;
    while ((c = getchar()) != '\n' && c != EOF) {}
}

int get_next_id(FILE *fp) {
    // Find max id in file and return max+1
    if (!fp) return 1;
    Product p;
    int maxid = 0;
    rewind(fp);
    while (fread(&p, sizeof(Product), 1, fp) == 1) {
        if (p.id > maxid) maxid = p.id;
    }
    return maxid + 1;
}

void press_enter_to_continue(void) {
    printf("\nPress Enter to continue...");
    getchar();
}

/* ========== Core operations ========== */

void add_product(void) {
    FILE *fp = fopen(DB_FILENAME, "ab+");
    if (!fp) {
        perror("Failed to open database file");
        return;
    }

    Product p;
    p.id = get_next_id(fp);

    printf("\n--- Add New Product (ID=%d) ---\n", p.id);
    printf("Name: ");
    fgets(p.name, sizeof p.name, stdin);
    p.name[strcspn(p.name, "\n")] = '\0';

    printf("Category: ");
    fgets(p.category, sizeof p.category, stdin);
    p.category[strcspn(p.category, "\n")] = '\0';

    printf("Quantity: ");
    while (scanf("%d", &p.quantity) != 1) {
        clear_input_buffer();
        printf("Invalid number. Quantity: ");
    }
    clear_input_buffer();

    printf("Price: ");
    while (scanf("%f", &p.price) != 1) {
        clear_input_buffer();
        printf("Invalid number. Price: ");
    }
    clear_input_buffer();

    printf("Description: ");
    fgets(p.description, sizeof p.description, stdin);
    p.description[strcspn(p.description, "\n")] = '\0';

    // append
    if (fwrite(&p, sizeof(Product), 1, fp) != 1) {
        perror("Failed to write product");
    } else {
        printf("Product added (ID=%d).\n", p.id);
    }

    fclose(fp);
    press_enter_to_continue();
}

void list_products(void) {
    Product *arr = NULL;
    size_t count = 0;
    if (!load_all_products(&arr, &count)) {
        printf("No products found or failed to read database.\n");
        press_enter_to_continue();
        return;
    }

    printf("\n--- Products List (%zu) ---\n", count);
    if (count == 0) {
        printf("No products found.\n");
    } else {
        printf("%-5s  %-20s  %-12s  %-8s  %-10s  %s\n",
               "ID", "Name", "Category", "Qty", "Price", "Description");
        printf("-------------------------------------------------------------------------------\n");
        for (size_t i = 0; i < count; ++i) {
            printf("%-5d  %-20s  %-12s  %-8d  %-10.2f  %s\n",
                arr[i].id, arr[i].name, arr[i].category, arr[i].quantity, arr[i].price, arr[i].description);
        }
    }

    free(arr);
    press_enter_to_continue();
}

void search_product(void) {
    int id;
    printf("\nEnter product ID to search: ");
    if (scanf("%d", &id) != 1) {
        clear_input_buffer();
        printf("Invalid input.\n");
        return;
    }
    clear_input_buffer();

    FILE *fp = fopen(DB_FILENAME, "rb");
    if (!fp) {
        printf("Database not found.\n");
        return;
    }
    Product p;
    int found = 0;
    while (fread(&p, sizeof(Product), 1, fp) == 1) {
        if (p.id == id) {
            printf("\nFound product (ID=%d):\n", p.id);
            printf("Name     : %s\n", p.name);
            printf("Category : %s\n", p.category);
            printf("Quantity : %d\n", p.quantity);
            printf("Price    : %.2f\n", p.price);
            printf("Desc     : %s\n", p.description);
            found = 1;
            break;
        }
    }
    fclose(fp);
    if (!found) printf("Product with ID %d not found.\n", id);
    press_enter_to_continue();
}

void update_product(void) {
    int id;
    printf("\nEnter product ID to update: ");
    if (scanf("%d", &id) != 1) {
        clear_input_buffer();
        printf("Invalid input.\n");
        return;
    }
    clear_input_buffer();

    Product *arr = NULL;
    size_t count = 0;
    if (!load_all_products(&arr, &count)) {
        printf("No products or failed to load.\n");
        return;
    }

    int idx = -1;
    for (size_t i = 0; i < count; ++i) {
        if (arr[i].id == id) { idx = (int)i; break; }
    }
    if (idx == -1) {
        printf("Product with ID %d not found.\n", id);
        free(arr);
        press_enter_to_continue();
        return;
    }

    printf("\n--- Update Product (ID=%d) ---\n", id);
    printf("Current name: %s\nNew name (enter to keep): ", arr[idx].name);
    char buf[MAX_NAME];
    fgets(buf, sizeof buf, stdin);
    if (buf[0] != '\n') {
        buf[strcspn(buf, "\n")] = '\0';
        strncpy(arr[idx].name, buf, MAX_NAME-1);
        arr[idx].name[MAX_NAME-1] = '\0';
    }

    printf("Current category: %s\nNew category (enter to keep): ", arr[idx].category);
    fgets(buf, sizeof buf, stdin);
    if (buf[0] != '\n') {
        buf[strcspn(buf, "\n")] = '\0';
        strncpy(arr[idx].category, buf, MAX_CAT-1);
        arr[idx].category[MAX_CAT-1] = '\0';
    }

    printf("Current quantity: %d\nNew quantity (enter  -1 to keep): ", arr[idx].quantity);
    int q;
    if (scanf("%d", &q) == 1) {
        if (q >= 0) arr[idx].quantity = q;
    }
    clear_input_buffer();

    printf("Current price: %.2f\nNew price (enter -1 to keep): ", arr[idx].price);
    float pr;
    if (scanf("%f", &pr) == 1) {
        if (pr >= 0.0f) arr[idx].price = pr;
    }
    clear_input_buffer();

    printf("Current description: %s\nNew description (enter to keep): ", arr[idx].description);
    char bufdesc[MAX_DESC];
    fgets(bufdesc, sizeof bufdesc, stdin);
    if (bufdesc[0] != '\n') {
        bufdesc[strcspn(bufdesc, "\n")] = '\0';
        strncpy(arr[idx].description, bufdesc, MAX_DESC-1);
        arr[idx].description[MAX_DESC-1] = '\0';
    }

    if (!save_all_products(arr, count)) {
        printf("Failed to save changes.\n");
    } else {
        printf("Product updated.\n");
    }

    free(arr);
    press_enter_to_continue();
}

void delete_product(void) {
    int id;
    printf("\nEnter product ID to delete: ");
    if (scanf("%d", &id) != 1) {
        clear_input_buffer();
        printf("Invalid input.\n");
        return;
    }
    clear_input_buffer();

    Product *arr = NULL;
    size_t count = 0;
    if (!load_all_products(&arr, &count)) {
        printf("No products or failed to load.\n");
        return;
    }

    size_t newcount = 0;
    for (size_t i = 0; i < count; ++i) {
        if (arr[i].id != id) {
            arr[newcount++] = arr[i];
        }
    }

    if (newcount == count) {
        printf("Product with ID %d not found (nothing deleted).\n", id);
    } else {
        if (!save_all_products(arr, newcount)) {
            printf("Failed to save database after deletion.\n");
        } else {
            printf("Product with ID %d deleted.\n", id);
        }
    }

    free(arr);
    press_enter_to_continue();
}

/* Export a plain text report for review/printing */
void export_text_report(void) {
    Product *arr = NULL;
    size_t count = 0;
    if (!load_all_products(&arr, &count)) {
        printf("No products found or failed to read database.\n");
        press_enter_to_continue();
        return;
    }

    FILE *out = fopen("products_report.txt", "w");
    if (!out) {
        perror("Unable to create report file");
        free(arr);
        press_enter_to_continue();
        return;
    }

    fprintf(out, "Product Report (%zu entries)\n", count);
    fprintf(out, "ID\tName\tCategory\tQty\tPrice\tDescription\n");
    fprintf(out, "---------------------------------------------------------------\n");
    for (size_t i = 0; i < count; ++i) {
        fprintf(out, "%d\t%s\t%s\t%d\t%.2f\t%s\n",
                arr[i].id, arr[i].name, arr[i].category, arr[i].quantity, arr[i].price, arr[i].description);
    }

    fclose(out);
    free(arr);
    printf("Report exported to products_report.txt\n");
    press_enter_to_continue();
}

/* ========== File helpers ========== */

/*
 * load_all_products
 *   - allocates an array and sets count; caller must free
 *   - returns 1 on success (even if count==0), 0 on failure
 */
int load_all_products(Product **arr, size_t *count) {
    *arr = NULL;
    *count = 0;

    FILE *fp = fopen(DB_FILENAME, "rb");
    if (!fp) {
        // treat as empty DB (no file)
        return 1;
    }

    if (fseek(fp, 0, SEEK_END) != 0) {
        fclose(fp);
        return 0;
    }
    long fsize = ftell(fp);
    if (fsize < 0) {
        fclose(fp);
        return 0;
    }
    rewind(fp);

    size_t n = (size_t)(fsize / sizeof(Product));
    if (n == 0) {
        fclose(fp);
        *arr = NULL;
        *count = 0;
        return 1;
    }

    Product *tmp = (Product *)malloc(n * sizeof(Product));
    if (!tmp) {
        perror("malloc");
        fclose(fp);
        return 0;
    }

    size_t read = fread(tmp, sizeof(Product), n, fp);
    if (read != n) {
        // partial read -- still proceed with what we have
        n = read;
    }
    fclose(fp);

    *arr = tmp;
    *count = n;
    return 1;
}

/*
 * save_all_products
 *   - writes array back to DB_FILENAME (overwrites)
 *   - returns 1 on success, 0 on failure
 */
int save_all_products(Product *arr, size_t count) {
    FILE *fp = fopen(DB_FILENAME, "wb");
    if (!fp) {
        perror("Failed to open DB for writing");
        return 0;
    }
    if (count > 0) {
        size_t written = fwrite(arr, sizeof(Product), count, fp);
        if (written != count) {
            perror("fwrite");
            fclose(fp);
            return 0;
        }
    }
    fclose(fp);
    return 1;
}