import os
import ast
import re
from flask_login import login_required
import openpyxl
from flask import Blueprint, request, render_template, redirect, url_for, jsonify, current_app
from flask_login import current_user
from model.Log import LogHelper
import config  # your database connection module

excel_bp = Blueprint('excel', __name__)

# Default folder in case config not set
DEFAULT_UPLOAD_FOLDER = 'uploads'


def get_upload_folder():
    """Returns the upload folder from Flask config or default, ensures it exists."""
    folder = current_app.config.get('UPLOAD_FOLDER', DEFAULT_UPLOAD_FOLDER)
    if not os.path.exists(folder):
        os.makedirs(folder)
    return folder


# ---------------- Upload Excel File ----------------
@excel_bp.route('/upload_excel_file', methods=['GET', 'POST'])
@login_required
def upload():
    if request.method == 'POST':
        file = request.files.get('file')
        if file and file.filename.endswith('.xlsx'):
            upload_folder = get_upload_folder()
            filepath = os.path.join(upload_folder, file.filename)
            file.save(filepath)

            LogHelper.log_action(
                "Upload Excel File",
                f"User {current_user.id} Upload Excel File '{file.filename}'"
            )
            return redirect(url_for('excel.show_table', filename=file.filename))
    return render_template('uploadExcelFile.html')


# ---------------- Show Excel Table ----------------
@excel_bp.route('/show_table/<filename>')
def show_table(filename):
        global data
        data = []

        filepath = os.path.join(get_upload_folder(), filename)
        wb = openpyxl.load_workbook(filepath, data_only=True)
        sheet = wb.active

        file_info = {
            "Subcontractor": sheet.cell(row=1, column=2).value,
            "State": sheet.cell(row=2, column=2).value,
            "District": sheet.cell(row=3, column=2).value,
            "Block": sheet.cell(row=4, column=2).value,
        }

        errors = []
        subcontractor_data = None
        state_data = None
        district_data = None
        block_data = None

        connection = config.get_db_connection()
        if connection:
            try:
                cursor = connection.cursor(dictionary=True)

                print(f"Calling GetStateByName with: {file_info['State']}")
                cursor.callproc('GetStateByName', [file_info['State']])
                for result in cursor.stored_results():
                    state_data = result.fetchone()
                if not state_data:
                    errors.append(f"State '{file_info['State']}' is not valid. Please add it.")

                if state_data:
                    print(f"Calling GetDistrictByNameAndStates with: {file_info['District']}, {state_data['State_ID']}")
                    cursor.callproc('GetDistrictByNameAndStates', [file_info['District'], state_data['State_ID']])
                    for result in cursor.stored_results():
                        district_data = result.fetchone()
                    if not district_data:
                        errors.append(f"District '{file_info['District']}' is not valid under state '{file_info['State']}'.")

                if district_data:
                    print(f"Calling GetBlockByNameAndDistricts with: {file_info['Block']}, {district_data['District_ID']}")
                    cursor.callproc('GetBlockByNameAndDistricts', [file_info['Block'], district_data['District_ID']])
                    for result in cursor.stored_results():
                        block_data = result.fetchone()
                    if not block_data:
                        errors.append(f"Block '{file_info['Block']}' is not valid under district '{file_info['District']}'.")

                print(f"Calling GetSubcontractorByName with: {file_info['Subcontractor']}")
                cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']])
                for result in cursor.stored_results():
                    subcontractor_data = result.fetchone()

                if not subcontractor_data:
                    print(f"Inserting subcontractor: {file_info['Subcontractor']}")
                    cursor.callproc('InsertSubcontractor', [file_info['Subcontractor']])
                    connection.commit()
                    cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']])
                    for result in cursor.stored_results():
                        subcontractor_data = result.fetchone()

                print("Calling GetAllHoldTypes")
                cursor.callproc("GetAllHoldTypes")
                hold_types_data = []
                for ht in cursor.stored_results():
                    hold_types_data = ht.fetchall()
                hold_types_lookup = {row['hold_type'].lower(): row['hold_type_id'] for row in hold_types_data if row['hold_type']}

                cursor.close()
            except Exception as e:
                print(f"Database error: {e}")
                return f"Database operation failed: {e}", 500
            finally:
                connection.close()

        variables = {}
        hold_columns = []
        hold_counter = 0

        for j in range(1, sheet.max_column + 1):
            col_value = sheet.cell(row=5, column=j).value
            if col_value:
                variables[col_value] = j
                if 'hold' in str(col_value).lower():
                    hold_counter += 1
                    hold_type_key = str(col_value).lower().strip()
                    hold_type_id = hold_types_lookup.get(hold_type_key)
                    hold_columns.append({
                        'column_name': col_value,
                        'column_number': j,
                        'hold_type_id': hold_type_id
                    })

        for i in range(6, sheet.max_row + 1):
            row_data = {}
            if sheet.cell(row=i, column=1).value:
                row_data["Row Number"] = i
                for var_name, col_num in variables.items():
                    row_data[var_name] = sheet.cell(row=i, column=col_num).value
                if sum(1 for value in row_data.values() if value) >= 4:
                    data.append(row_data)

        for hold in hold_columns:
            if hold['hold_type_id']:
                print(f" if Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}")
            else:
                errors.append(f"Hold Type not added ! Column name '{hold['column_name']}'.")
                print(f" else Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}")

        return render_template(
            'show_excel_file.html',
            file_info=file_info,
            variables=variables,
            data=data,
            subcontractor_data=subcontractor_data,
            state_data=state_data,
            district_data=district_data,
            block_data=block_data,
            errors=errors,
            hold_columns=hold_columns,
            hold_counter=hold_counter
        )


 # save Excel data
@excel_bp.route('/save_data', methods=['POST'])
def save_data():
    # Extract form data
    subcontractor_id = request.form.get("subcontractor_data")
    state_id = request.form.get("state_data")
    district_id = request.form.get("district_data")
    block_id = request.form.get("block_data")
    variables = request.form.getlist('variables[]')
    hold_columns = request.form.get("hold_columns")
    hold_counter = request.form.get("hold_counter")
    if not data:
        return jsonify({"error": "No data provided to save"}), 400
    if data:
        connection = config.get_db_connection()
        cursor = connection.cursor()
        try:
            for entry in data:
                save_data = {
                    "PMC_No": entry.get("PMC_No"),
                    "Invoice_Details": entry.get("Invoice_Details", ''),
                    "Work_Type": 'none',
                    "Invoice_Date": entry.get("Invoice_Date").strftime('%Y-%m-%d') if entry.get(
                    "Invoice_Date") else None,
                    "Invoice_No": entry.get("Invoice_No", ''),
                    "Basic_Amount": entry.get("Basic_Amount", 0.00),
                    "Debit_Amount": entry.get("Debit_Amount", 0.00),
                    "After_Debit_Amount": entry.get("After_Debit_Amount", 0.00),
                    "Amount": entry.get("Amount", 0.00),
                    "GST_Amount": entry.get("GST_Amount", 0.00),
                    "TDS_Amount": entry.get("TDS_Amount", 0.00),
                    "SD_Amount": entry.get("SD_Amount", 0.00),
                    "On_Commission": entry.get("On_Commission", 0.00),
                    "Hydro_Testing": entry.get("Hydro_Testing", 0.00),
                    "Hold_Amount": 0,
                    "GST_SD_Amount": entry.get("GST_SD_Amount", 0.00),
                    "Final_Amount": entry.get("Final_Amount", 0.00),
                    "Payment_Amount": entry.get("Payment_Amount", 0.00),
                    "Total_Amount": entry.get("Total_Amount", 0.00),
                    "TDS_Payment_Amount": entry.get("TDS_Payment_Amount", 0.00),
                    "UTR": entry.get("UTR", ''),
                }
                village_name, work_type = None, None
                village_id = 0
                LogHelper.log_action("Data saved", f"User {current_user.id} Data saved'{  village_name}'")
                PMC_No = save_data.get('PMC_No')
                Invoice_Details = save_data.get('Invoice_Details')
                Invoice_Date = save_data.get('Invoice_Date')
                Invoice_No = save_data.get('Invoice_No')
                Basic_Amount = save_data.get('Basic_Amount')
                Debit_Amount = save_data.get('Debit_Amount')
                After_Debit_Amount = save_data.get('After_Debit_Amount')
                Amount = save_data.get('Amount')
                GST_Amount = save_data.get('GST_Amount')
                TDS_Amount = save_data.get('TDS_Amount')
                SD_Amount = save_data.get('SD_Amount')
                On_Commission = save_data.get('On_Commission')
                Hydro_Testing = save_data.get('Hydro_Testing')
                GST_SD_Amount = save_data.get('GST_SD_Amount')
                Final_Amount = save_data.get('Final_Amount')
                Payment_Amount = save_data.get('Payment_Amount')
                Total_Amount = save_data.get('Total_Amount')
                TDS_Payment_Amount = save_data.get('TDS_Payment_Amount')
                UTR = save_data.get('UTR')
                
                if Invoice_Details:
                    words = Invoice_Details.lower().split()
                    if 'village' in words:
                        village_pos = words.index('village')
                        village_name = " ".join(words[:village_pos])
                    if 'work' in words:
                        work_pos = words.index('work')
                        if village_name:
                            work_type = " ".join(words[village_pos + 1:work_pos + 1])
                        else:
                            work_type = " ".join(words[:work_pos + 1])
                    if Invoice_Details and 'village' in Invoice_Details.lower() and 'work' in Invoice_Details.lower():
                        print("village_name ::", village_name, "|| work_type ::", work_type)
                        if block_id and village_name:
                            village_id = None
                            cursor.callproc("GetVillageId", (block_id, village_name))
                            for result in cursor.stored_results():
                                result = result.fetchone()
                            village_id = result[0] if result else None
                            if not village_id:
                                cursor.callproc("SaveVillage", (village_name, block_id))
                                cursor.callproc("GetVillageId", (block_id, village_name))
                                for result in cursor.stored_results():
                                    result = result.fetchone()
                                village_id = result[0] if result else None
                        print("village_id :", village_id)
                        print("block_id :", block_id)
                        print("invoice :", PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No,
                              Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount,
                              SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount)

                        args = (
                            PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No,
                            Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount,
                            SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount,
                            subcontractor_id, 0
                        )

                        print("All invoice Details ",args)
                        results = cursor.callproc('SaveInvoice', args)
                        invoice_id = results[-1]
                        print("invoice id from the excel ", invoice_id)
                        if isinstance(hold_columns, str):
                            hold_columns = ast.literal_eval(hold_columns)
                        if isinstance(hold_columns, list) and all(isinstance(hold, dict) for hold in hold_columns):
                            for hold in hold_columns:
                                print(f"Processing hold: {hold}")
                                hold_column_name = hold.get('column_name')  # Get column name
                                hold_type_id = hold.get('hold_type_id')  # Get hold_type_id
                                if hold_column_name:
                                    hold_amount = entry.get(
                                        hold_column_name)  # Get the value for that specific hold column
                                    if hold_amount is not None:
                                        print(f"Processing hold type: {hold_column_name}, Hold Amount: {hold_amount}")
                                        hold_join_data = {
                                            "Contractor_Id": subcontractor_id,
                                            "Invoice_Id": invoice_id,
                                            "hold_type_id": hold_type_id,
                                            "hold_amount": hold_amount
                                        }
                                        cursor.callproc('InsertHoldJoinData', [
                                            hold_join_data['Contractor_Id'], hold_join_data['Invoice_Id'],
                                            hold_join_data['hold_type_id'], hold_join_data['hold_amount']
                                        ])
                                        connection.commit()
                                        print(f"Inserted hold join data: {hold_join_data}")
                                else:
                                    print(f"Invalid hold entry: {hold}")
                        else:
                            print("Hold columns data is not a valid list of dictionaries.")
#---------------------------------------------Credit Note---------------------------------------------------------------------------
                    elif any(keyword in Invoice_Details.lower() for keyword in ['credit note','logging report']):
                        print("Credit note found:", PMC_No, Invoice_No, Basic_Amount, Debit_Amount, Final_Amount,
                              After_Debit_Amount, GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, Invoice_No)
                        cursor.callproc(
                            'AddCreditNoteFromExcel',
                            [
                                PMC_No, Invoice_Details, Basic_Amount, Debit_Amount, After_Debit_Amount, 
                                GST_Amount, Amount, Final_Amount, Payment_Amount, Total_Amount, UTR, 
                                subcontractor_id, Invoice_No
                            ]
                        )
#-----------------------------------------------Hold Amount----------------------------------------------------------------------
                    # Step 1: Normalize Invoice_Details: lowercase, trim, remove extra spaces
                    normalized_details = re.sub(r'\s+', ' ', Invoice_Details.strip()).lower()
                    # Step 2: Define lowercase keywords
                    keywords = [
                        'excess hold',
                        'ht',
                        'hold release amount',
                        'dpr excess hold amount',
                        'excess hold amount',
                        'Multi to Single layer bill',
                        'hold amount',
                        'logging report'
                    ]
                    # Step 3: Matching condition
                    if any(kw in normalized_details for kw in keywords):
                        print("✅ Match found. Inserting hold release for:", Invoice_Details)
                        cursor.callproc(
                            'AddHoldReleaseFromExcel',
                            [PMC_No, Invoice_No, Invoice_Details, Basic_Amount, Final_Amount, UTR, subcontractor_id]
)
                        connection.commit() 
                        print("✅ Hold release inserted for:", PMC_No, Invoice_Details)
                    #------------------------------------------------------------------------------------------------------------------
                    elif Invoice_Details and any(
                            keyword in Invoice_Details.lower() for keyword in ['gst', 'release', 'note']):
                        print("Gst rels :", PMC_No, Invoice_No, Basic_Amount, Final_Amount,Total_Amount,UTR, subcontractor_id)
                        cursor.callproc(
                            'AddGSTReleaseFromExcel',
                            [PMC_No, Invoice_No, Basic_Amount, Final_Amount, Total_Amount, UTR, subcontractor_id]
                        )

                if PMC_No and Total_Amount and UTR:
                    print("Payment :", PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR )
                    cursor.callproc("SavePayment",(PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR ))
                if not village_id:
                    village_id = None
                cursor.callproc('InsertOrUpdateInPayment', (
                        PMC_No,
                        village_id,
                        work_type,
                        Invoice_Details,
                        Invoice_Date,
                        Invoice_No,
                        Basic_Amount,
                        Debit_Amount,
                        After_Debit_Amount,
                        Amount,
                        GST_Amount,
                        TDS_Amount,
                        SD_Amount,
                        On_Commission,
                        Hydro_Testing,
                        0,
                        GST_SD_Amount,
                        Final_Amount,
                        Payment_Amount,
                        TDS_Payment_Amount,
                        Total_Amount,
                        UTR,
                        subcontractor_id
                    ))
            connection.commit()
            return jsonify({"success": "Data saved successfully!"}), 200
        except Exception as e:
            connection.rollback()
            return jsonify({"error": f"An unexpected error occurred: {e}"}), 500
        finally:
            cursor.close()
            connection.close()
    return render_template('index.html')
# ---------------------- Report --------------------------------